diff options
-rw-r--r-- | FAQ.rst | 230 | ||||
-rw-r--r-- | Makefile.am | 8 | ||||
-rwxr-xr-x | autogen.sh | 5 | ||||
-rwxr-xr-x | bindings/python/tests/profiles_tests.py | 4 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | docs/Makefile.am | 2 | ||||
-rw-r--r-- | docs/reference/lasso/Makefile.am | 2 | ||||
-rw-r--r-- | lasso.pc.in | 2 | ||||
-rw-r--r-- | lasso/saml-2.0/logout.c | 3 | ||||
-rw-r--r-- | lasso/utils.h | 3 | ||||
-rw-r--r-- | lasso/xml/private.h | 1 | ||||
-rw-r--r-- | lasso/xml/saml-2.0/saml2_key_info_confirmation_data_type.c | 3 | ||||
-rw-r--r-- | lasso/xml/saml-2.0/samlp2_logout_request.c | 8 | ||||
-rw-r--r-- | lasso/xml/saml-2.0/samlp2_manage_name_id_request.c | 18 | ||||
-rw-r--r-- | lasso/xml/xml.c | 148 | ||||
-rw-r--r-- | tests/basic_tests.c | 26 | ||||
-rwxr-xr-x | tools/git-version-gen | 225 | ||||
-rw-r--r-- | website/web/index.xml | 2 |
18 files changed, 630 insertions, 65 deletions
diff --git a/FAQ.rst b/FAQ.rst new file mode 100644 index 00000000..638482f2 --- /dev/null +++ b/FAQ.rst @@ -0,0 +1,230 @@ +Lasso FAQ +========= + +Generalities +------------ + +1. What is Lasso ? + + Lasso is a C library which implements the identity federation and single-sign + on protocol standards ID-FF 1.2 and SAML 2.0. It also implements attribute + exchange + +2. What does Lasso mean ? + + Lasso is the acronym of Liberty Alliance Single Sign On. + +2. What is Liberty Alliance ? + + It'a consortium built to propose a common XML standard for transmitting + information about authentication and identity, made in response to the + Microsoft Passport technology. It has since been dismantled and all its assets + are now managed by the Oasis standard body and the Kantara initiative. + + The more recent standard coming from the initial Liberty Alliance initiative + is SAML 2.0. + +Use of the library +------------------ + +1. How to make a simple POST assertion consumer using Python ? + +Using Python&WSGI: + +.. code-block:: python + + import sys + import lasso + from wsgiref.simple_server import make_server + import logging + import urlparse + + logging.basicConfig(level=logging.DEBUG) + + sp_metadata_xml = '''<?xml version="1.0"?> + <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:ds="http://www.w3.org/2000/09/xmldsig#" + entityID="http://localhost:8081/metadata"> + <SPSSODescriptor + AuthnRequestsSigned="true" + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + + <AssertionConsumerService isDefault="true" index="0" + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="http://localhost:8081/singleSignOnPost" /> + <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat> + </SPSSODescriptor> + <Organization> + <OrganizationName xml:lang="en">Example SAML 2.0 metadatas</OrganizationName> + </Organization> + </EntityDescriptor>''' + + idp_metadata_xml = '''<?xml version="1.0"?> + <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:ds="http://www.w3.org/2000/09/xmldsig#" + entityID="http://localhost:3001/saml/metadata"> + + + <IDPSSODescriptor + WantAuthnRequestsSigned="true" + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <KeyDescriptor use="signing"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> + <RSAKeyValue> + <Modulus>4yalpsp9Sxlsj07PEI8jJxhSJdo4F0iW0H8u1dhwmsW5YQvRUw/yPlmC09q4WjImmnFVNCJarAOYeFgQCxfIoBasKNnUeBQpogo8W0Q/3mCuKl6lNSr/PIuxMVVNPDWmWkhHXJx/MVar2IREKa1P4jHL0Uxl69/idLwc7TtK1h8=</Modulus> + <Exponent>AQAB</Exponent> + </RSAKeyValue> + </KeyValue> + </ds:KeyInfo> + </KeyDescriptor> + <KeyDescriptor use="encryption"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> + <RSAKeyValue> + <Modulus>wLu5SdmwyS4o1On/aw4nElLGERFG931exvkzu0ewaM1/oUyD3dO7UC5xMGnPfc6IaH5BcJc3fLr6PJhX55ZrMR98ToPwoUFwuLKK43exwYBEBOOMe1CrCB/Bq+EH6/2sKNXKfgJqj06/3yzafLRiWpMxy2isllxMAvaZXrkpm4c=</Modulus> + <Exponent>AQAB</Exponent> + </RSAKeyValue> + </KeyValue> + </ds:KeyInfo> + </KeyDescriptor> + </IDPSSODescriptor> + + </EntityDescriptor> + ''' + + def app(environ, start_response): + server = lasso.Server.newFromBuffers(sp_metadata_xml) + server.addProviderFromBuffer(lasso.PROVIDER_ROLE_IDP, idp_metadata_xml) + login = lasso.Login(server) + try: + data = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) + qs = urlparse.parse_qs(data) + try: + login.processAuthnResponseMsg(qs['SAMLResponse'][0]) + except (lasso.DsError, lasso.ProfileCannotVerifySignatureError): + raise Exception('Invalid signature') + except lasso.Error: + raise Exception('Misc error') + try: + login.acceptSso() + except lasso.Error: + raise Exception('Invalid assertion') + except Exception, e: + start_response('500 Internal Error', [('content-type', 'text/plain')], + sys.exc_info()) + return ['Erreur: ', str(e)] + else: + start_response('200 Ok', [('content-type', 'text/plain')], sys.exc_info()) + return ['You are identified as ', login.assertion.subject.nameId.content] + + s = make_server('0.0.0.0', 8081, app) + s.serve_forever() + +2. How to make a simple POST assertion consumer using PHP5 ? + +Put the following content in a file named index.php: + +.. code-block:: php + + <? + require "lasso.php"; + + $sp_metadata_xml = <<<'XML' + <?xml version="1.0"?> + <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:ds="http://www.w3.org/2000/09/xmldsig#" + entityID="http://yourdomain.com/index.php?metadata"> + <SPSSODescriptor + AuthnRequestsSigned="true" + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + + <AssertionConsumerService isDefault="true" index="0" + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="http://yourdomain.com/index.php?assertion_consumer" /> + <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat> + </SPSSODescriptor> + <Organization> + <OrganizationName xml:lang="en">Example SAML 2.0 metadatas</OrganizationName> + </Organization> + </EntityDescriptor> + XML; + + $idp_metadata_xml = <<<'XML' + <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:ds="http://www.w3.org/2000/09/xmldsig#" + entityID="http://localhost:3001/saml/metadata"> + + + <IDPSSODescriptor + WantAuthnRequestsSigned="true" + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <KeyDescriptor use="signing"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> + <RSAKeyValue> + <Modulus>4yalpsp9Sxlsj07PEI8jJxhSJdo4F0iW0H8u1dhwmsW5YQvRUw/yPlmC09q4WjImmnFVNCJarAOYeFgQCxfIoBasKNnUeBQpogo8W0Q/3mCuKl6lNSr/PIuxMVVNPDWmWkhHXJx/MVar2IREKa1P4jHL0Uxl69/idLwc7TtK1h8=</Modulus> + <Exponent>AQAB</Exponent> + </RSAKeyValue> + </KeyValue> + </ds:KeyInfo> + </KeyDescriptor> + <KeyDescriptor use="encryption"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> + <RSAKeyValue> + <Modulus>wLu5SdmwyS4o1On/aw4nElLGERFG931exvkzu0ewaM1/oUyD3dO7UC5xMGnPfc6IaH5BcJc3fLr6PJhX55ZrMR98ToPwoUFwuLKK43exwYBEBOOMe1CrCB/Bq+EH6/2sKNXKfgJqj06/3yzafLRiWpMxy2isllxMAvaZXrkpm4c=</Modulus> + <Exponent>AQAB</Exponent> + </RSAKeyValue> + </KeyValue> + </ds:KeyInfo> + </KeyDescriptor> + </IDPSSODescriptor> + + </EntityDescriptor> + XML; + + if (isset($_GET["metadata"])) { + header('Content-Type: text/xml'); + echo $sp_metadata_xml; + exit(0); + } + + if (isset($_GET["assertion_consumer"])) { + $server = LassoServer::newFromBuffers($sp_metadata_xml); + $server->addProviderFromBuffer(LASSO_PROVIDER_ROLE_IDP, $idp_metadata_xml); + $login = new LassoLogin($server); + + function error($msg) { + header("HTTP/1.0 500 Internal Error"); + ?> <h1>Erreur:</h1><pre> <? echo htmlentities($msg); ?></pre><? + exit(0); + } + + try { + try { + $login->processAuthnResponseMsg($_POST["SAMLResponse"]); + } catch (LassoDsError $e) { + error('Invalid signature'); + } catch (LassoProfileCannotVerifySignatureError $e) { + error('Invalid signature'); + } catch (LassoError $e) { + error('Misc error, ' . $e); + } + try { + $login->acceptSso(); + } catch (LassoError $e) { + error('Invalid assertion'); + } + } catch (Exception $e) { + error('Unexpected error: ' . $e); + } + ?> You are identified as <? echo $login->assertion->subject->nameId->content; + +You must replace the ``$idp_metadata_xml`` variable by your identity provider metadata. +You can indicate to your identity provider the URL +http://yourdomain.com/index.php?metadata as the URL of your metadata file. diff --git a/Makefile.am b/Makefile.am index 3aecc4d5..caca2f7d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,7 +7,9 @@ SUBDIRS = lasso \ docs EXTRA_DIST = COPYING lasso.pc.in lasso-src-config.in autogen.sh tools abi \ - lasso.doap README.JAVA README.WIN32 HACKING logos + lasso.doap README.JAVA README.WIN32 HACKING logos $(top_srcdir)/.version + +BUILT_SOURCES = $(top_srcdir)/.version MAINTAINERCLEANFILES = \ Makefile.in \ @@ -36,5 +38,9 @@ dist-hook: else \ echo A git clone is required to generate a ChangeLog >&2; \ fi + echo $(VERSION) > $(distdir)/.tarball-version DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc + +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ @@ -27,7 +27,10 @@ cd "$srcdir" DIE=1 } -if automake-1.11 --version < /dev/null > /dev/null 2>&1; then +if automake-1.12 --version < /dev/null > /dev/null 2>&1; then + AUTOMAKE=automake-1.12 + ACLOCAL=aclocal-1.12 +elif automake-1.11 --version < /dev/null > /dev/null 2>&1; then AUTOMAKE=automake-1.11 ACLOCAL=aclocal-1.11 elif automake-1.10 --version < /dev/null > /dev/null 2>&1; then diff --git a/bindings/python/tests/profiles_tests.py b/bindings/python/tests/profiles_tests.py index 0068d841..e86a6195 100755 --- a/bindings/python/tests/profiles_tests.py +++ b/bindings/python/tests/profiles_tests.py @@ -35,6 +35,9 @@ if not '../.libs' in sys.path: sys.path.insert(0, '../.libs') import lasso +import logging + +logging.basicConfig() try: @@ -482,6 +485,7 @@ class LogoutTestCase(unittest.TestCase): '''Test parsing of a logout request with more than one session index''' content = '''<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="xxxx" Version="2.0" IssueInstant="2010-06-14T22:00:00"> <saml:Issuer>me</saml:Issuer> + <saml:NameID>coin</saml:NameID> <samlp:SessionIndex>id1</samlp:SessionIndex> <samlp:SessionIndex>id2</samlp:SessionIndex> <samlp:SessionIndex>id3</samlp:SessionIndex> diff --git a/configure.ac b/configure.ac index 894b6ab1..a088547c 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ dnl - Second number is the number of supported API versions where API version > dnl first number. dnl - Third number is the current API version implementation version number. dnl See libtool explanations about current, age and release, later in this file. -AC_INIT([lasso], 2.4.0, lasso-devel@lists.labs.libre-entreprise.org) +AC_INIT([lasso], m4_esyscmd([tools/git-version-gen .tarball-version]), lasso-devel@lists.labs.libre-entreprise.org) dnl Check if autoconf ver > 2.53 AC_PREREQ(2.53) AC_CONFIG_MACRO_DIR([m4]) @@ -136,7 +136,6 @@ AC_CHECK_PROGS(PYTHON, python) AC_CHECK_PROGS(SWIG, swig) dnl Make sure we have an ANSI compiler -AM_C_PROTOTYPES test "z$U" != "z" && AC_MSG_ERROR(Compiler not ANSI compliant) dnl Check for variadic macros @@ -160,7 +159,7 @@ dnl ========================================================================== changequote(<<, >>)dnl VERSION_MAJOR=`echo $VERSION | $SED -e 's/^\([^\.]*\)\.\([^\.]*\)\.\(.*\)$/\1/'` VERSION_MINOR=`echo $VERSION | $SED -e 's/^\([^\.]*\)\.\([^\.]*\)\.\(.*\)$/\2/'` -VERSION_RELEASE=`echo $VERSION | $SED -e 's/^\([^\.]*\)\.\([^\.]*\)\.\(.*\)$/\3/'` +VERSION_RELEASE=`echo $VERSION | $SED -e 's/^\([^\.]*\)\.\([^\.]*\)\.\([0-9]*\).*$/\3/'` changequote([, ])dnl VERSION_UNDERSCORED=`echo $VERSION | $SED -e 's/\./_/g'` AC_SUBST(VERSION_UNDERSCORED) diff --git a/docs/Makefile.am b/docs/Makefile.am index 49c40034..e7265177 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -5,6 +5,6 @@ SUBDIRS = lasso-book if ENABLE_GTK_DOC SUBDIRS += reference else -DIST_SUBDIRS = reference +DIST_SUBDIRS = reference lasso-book endif diff --git a/docs/reference/lasso/Makefile.am b/docs/reference/lasso/Makefile.am index 4713c3c7..ac8c5118 100644 --- a/docs/reference/lasso/Makefile.am +++ b/docs/reference/lasso/Makefile.am @@ -90,8 +90,6 @@ GTKDOC_LIBS= \ $(top_builddir)/lasso/liblasso.la \ $(LASSO_LIBS) -EXTRA_DIST = - # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make diff --git a/lasso.pc.in b/lasso.pc.in index 6cfc4a4b..a1a830a8 100644 --- a/lasso.pc.in +++ b/lasso.pc.in @@ -6,6 +6,6 @@ includedir=@includedir@ Name: lasso Version: @VERSION@ Description: A free implementation of the Liberty Alliance specifications -Requires: libxml-2.0 libxslt xmlsec1 glib-2.0 gobject-2.0 +Requires.private: libxml-2.0 libxslt xmlsec1 glib-2.0 gobject-2.0 Cflags: -I${includedir} @LASSO_PUB_CFLAGS@ Libs: -L${libdir} @LASSO_CORE_LIBS@ diff --git a/lasso/saml-2.0/logout.c b/lasso/saml-2.0/logout.c index 2244d566..ab47e357 100644 --- a/lasso/saml-2.0/logout.c +++ b/lasso/saml-2.0/logout.c @@ -321,6 +321,9 @@ lasso_saml20_logout_build_response_msg(LassoLogout *logout) LASSO_SAML2_STATUS_CODE_RESPONDER, LASSO_SAML2_STATUS_CODE_REQUEST_DENIED)); } + } else { + lasso_check_good_rc(lasso_profile_saml20_setup_message_signature( + profile, profile->response)); } /* build logout response message */ diff --git a/lasso/utils.h b/lasso/utils.h index d62d3cdb..9526e0ed 100644 --- a/lasso/utils.h +++ b/lasso/utils.h @@ -123,6 +123,9 @@ #define lasso_release_list(dest) \ lasso_release_full2(dest, g_list_free, GList*) +#define lasso_release_slist(dest) \ + lasso_release_full2(dest, g_slist_free, GSList*) + #define lasso_release_list_of_full(dest, free_function) \ { \ GList **__tmp = &(dest); \ diff --git a/lasso/xml/private.h b/lasso/xml/private.h index 629d6ca5..b1ec0979 100644 --- a/lasso/xml/private.h +++ b/lasso/xml/private.h @@ -62,6 +62,7 @@ typedef enum { SNIPPET_ALLOW_TEXT = 1 << 26, /* allow text childs in list of nodes */ SNIPPET_KEEP_XMLNODE = 1 << 27, /* force keep xmlNode */ SNIPPET_PRIVATE = 1 << 28, /* means that the offset is relative to a private extension */ + SNIPPET_MANDATORY = 1 << 29, /* means that the element cardinality is at least 1 */ } SnippetType; typedef enum { diff --git a/lasso/xml/saml-2.0/saml2_key_info_confirmation_data_type.c b/lasso/xml/saml-2.0/saml2_key_info_confirmation_data_type.c index 901b709b..dbd0fb47 100644 --- a/lasso/xml/saml-2.0/saml2_key_info_confirmation_data_type.c +++ b/lasso/xml/saml-2.0/saml2_key_info_confirmation_data_type.c @@ -64,7 +64,8 @@ struct _LassoSaml2KeyInfoConfirmationDataTypePrivate { static struct XmlSnippet schema_snippets[] = { { "KeyInfo", SNIPPET_LIST_NODES|SNIPPET_PRIVATE, - G_STRUCT_OFFSET(LassoSaml2KeyInfoConfirmationDataTypePrivate, KeyInfo), "LassoDsKeyInfo", NULL, NULL}, + G_STRUCT_OFFSET(LassoSaml2KeyInfoConfirmationDataTypePrivate, KeyInfo), + "LassoDsKeyInfo", LASSO_DS_PREFIX, LASSO_DS_HREF}, {NULL, 0, 0, NULL, NULL, NULL} }; diff --git a/lasso/xml/saml-2.0/samlp2_logout_request.c b/lasso/xml/saml-2.0/samlp2_logout_request.c index 947e0cd6..3d123d65 100644 --- a/lasso/xml/saml-2.0/samlp2_logout_request.c +++ b/lasso/xml/saml-2.0/samlp2_logout_request.c @@ -139,8 +139,16 @@ init_from_xml(LassoNode *node, xmlNode *xmlnode) int rc = 0; xmlNode *child = NULL; LassoSamlp2LogoutRequestPrivate *pv = NULL; + LassoSamlp2LogoutRequest *logout_request = (LassoSamlp2LogoutRequest*)node; rc = parent_class->init_from_xml(node, xmlnode); + if ((logout_request->BaseID != 0) + + (logout_request->NameID != 0) + + (logout_request->EncryptedID != 0) != 1) { + error("samlp2:LogoutRequest needs one of BaseID, NameID or EncryptedID"); + rc = 1; + } + if (rc == 0) { pv = GET_PRIVATE(node); diff --git a/lasso/xml/saml-2.0/samlp2_manage_name_id_request.c b/lasso/xml/saml-2.0/samlp2_manage_name_id_request.c index 207f1444..678c6201 100644 --- a/lasso/xml/saml-2.0/samlp2_manage_name_id_request.c +++ b/lasso/xml/saml-2.0/samlp2_manage_name_id_request.c @@ -78,12 +78,30 @@ static struct XmlSnippet schema_snippets[] = { /* instance and class init functions */ /*****************************************************************************/ +static LassoNodeClass *parent_class = NULL; + +static int +init_from_xml(LassoNode *node, xmlNode *xmlnode) +{ + int rc = 0; + LassoSamlp2ManageNameIDRequest *nid_request = (LassoSamlp2ManageNameIDRequest*)node; + + rc = parent_class->init_from_xml(node, xmlnode); + if ((nid_request->NameID != 0) + + (nid_request->EncryptedID != 0) != 1) { + error("samlp2:LogoutRequest needs one of BaseID, NameID or EncryptedID"); + rc = 1; + } + return rc; +} static void class_init(LassoSamlp2ManageNameIDRequestClass *klass) { LassoNodeClass *nclass = LASSO_NODE_CLASS(klass); + parent_class = g_type_class_peek_parent(klass); + klass->parent.parent.init_from_xml = init_from_xml; nclass->node_data = g_new0(LassoNodeClassData, 1); lasso_node_class_set_nodename(nclass, "ManageNameIDRequest"); lasso_node_class_set_ns(nclass, LASSO_SAML2_PROTOCOL_HREF, LASSO_SAML2_PROTOCOL_PREFIX); diff --git a/lasso/xml/xml.c b/lasso/xml/xml.c index 4fe7a575..b7b29f95 100644 --- a/lasso/xml/xml.c +++ b/lasso/xml/xml.c @@ -1366,14 +1366,42 @@ is_snippet_type(struct XmlSnippet *snippet, SnippetType simple_type) { } static inline gboolean +is_snippet_mandatory(struct XmlSnippet *snippet) +{ + return snippet->type & SNIPPET_MANDATORY ? TRUE : FALSE; +} + +static inline gboolean +is_snippet_multiple(struct XmlSnippet *snippet) +{ + switch (snippet->type & 0xff) { + case SNIPPET_LIST_XMLNODES: + case SNIPPET_LIST_CONTENT: + case SNIPPET_LIST_NODES: + case SNIPPET_EXTENSION: + return TRUE; + default: + return FALSE; + } +} + +static inline gboolean node_match_snippet(xmlNode *parent, xmlNode *node, struct XmlSnippet *snippet) { + gboolean rc = TRUE; + /* special case of ArtifactResponse */ - if (snippet->type & SNIPPET_ANY) + if (snippet->type & SNIPPET_ANY) { return TRUE; - return (lasso_strisequal(snippet->name, (char*)node->name) - && ((!snippet->ns_uri && lasso_equal_namespace(parent->ns, node->ns)) || - (node->ns && lasso_strisequal((char*)node->ns->href, snippet->ns_uri)))); + } else { + rc = rc && lasso_strisequal(snippet->name, (char*)node->name); + rc = rc && + ((!snippet->ns_uri && + lasso_equal_namespace(parent->ns, node->ns)) || + (node->ns && + lasso_strisequal((char*)node->ns->href, snippet->ns_uri))); + return rc; + } } /** FIXME: return a real error code */ @@ -1396,16 +1424,20 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) xmlAttr *attr = NULL; GType g_type = 0; LassoNodeClass *node_class; + gint rc = 0; - if (! xmlnode) - return 1; + if (! xmlnode) { + rc = 1; + goto cleanup; + } node_class = class = LASSO_NODE_GET_CLASS(node); /* No node_data no initialization possible */ if (! class->node_data) { message(G_LOG_LEVEL_WARNING, "Class %s has no node_data so no initialization " "is possible", G_OBJECT_CLASS_NAME(class)); - return 0; + rc = 1; + goto cleanup; } /* Collect special snippets like SNIPPET_COLLECT_NAMESPACES, SNIPPET_ANY, SNIPPET_ATTRIBUTE @@ -1461,14 +1493,16 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) if (attr->ns && lasso_strisequal((char*)attr->name, "type") && lasso_strisequal((char*)attr->ns->href, LASSO_XSI_HREF)) { char *colon = strchr((char*)content, ':'); - xmlNs *ns; - *colon = '\0'; - ns = xmlSearchNs(NULL, xmlnode, content); - *colon = ':'; - if (ns && lasso_strisequal((char*)ns->href, (char*)node_class->node_data->ns->href) - && lasso_strisequal(&colon[1], node_class->node_data->node_name)) { - lasso_release_xml_string(content); - continue; + if (colon) { + xmlNs *ns; + *colon = '\0'; + ns = xmlSearchNs(NULL, xmlnode, content); + *colon = ':'; + if (ns && lasso_strisequal((char*)ns->href, (char*)node_class->node_data->ns->href) + && lasso_strisequal(&colon[1], node_class->node_data->node_name)) { + lasso_release_xml_string(content); + continue; + } } } @@ -1558,28 +1592,54 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) xmlNode *first_child = NULL; GList **list = NULL; xmlChar *content = NULL; + gboolean match = FALSE; + struct XmlSnippet *matched_snippet = NULL; - /* Find a matching snippet */ - while (class_iter && ! node_match_snippet(xmlnode, t, snippet)) { - snippet++; +#define ADVANCE \ + snippet++; \ next_node_snippet(&class_iter, &snippet); +#define ERROR \ + error("Element %s:%s cannot be parsed", \ + t->ns != NULL ? (char*)t->ns->prefix : "<noprefix>", \ + t->name); \ + rc = 1; \ + goto cleanup; + /* Find a matching snippet */ + while (class_iter && snippet) { + gboolean mandatory = is_snippet_mandatory(snippet); + gboolean multiple = is_snippet_multiple(snippet); + + if ((match = node_match_snippet(xmlnode, t, snippet))) { + matched_snippet = snippet; + class = class_iter->data; + g_type = G_TYPE_FROM_CLASS(class); + value = SNIPPET_STRUCT_MEMBER_P(node, g_type, snippet); + list = value; + if (! multiple) { + ADVANCE; + } + break; + } else { + if (mandatory) { + break; + } else { + ADVANCE; + } + } } - if (! class_iter) { - /* If we cannot find one, terminate here. */ - break; + if (! match) { + ERROR; } - class = class_iter->data; - g_type = G_TYPE_FROM_CLASS(class); - value = SNIPPET_STRUCT_MEMBER_P(node, g_type, snippet); - list = value; +#undef ADVANCE +#undef ERROR - if (snippet->offset || (snippet->type & SNIPPET_PRIVATE)) { - switch (snippet->type & 0xff) { + if (matched_snippet->offset || (matched_snippet->type & SNIPPET_PRIVATE)) { + switch (matched_snippet->type & 0xff) { case SNIPPET_LIST_NODES: case SNIPPET_NODE: subnode = lasso_node_new_from_xmlNode_with_type(t, - snippet->class_name); - if (is_snippet_type(snippet, SNIPPET_NODE)) { + matched_snippet->class_name); + if (is_snippet_type(matched_snippet, SNIPPET_NODE)) { lasso_assign_new_gobject(*(LassoNode**)value, subnode); } else { lasso_list_add_new_gobject(*list, subnode); @@ -1589,7 +1649,7 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) first_child = xmlSecGetNextElementNode(t->children); if (first_child) { subnode = lasso_node_new_from_xmlNode_with_type(first_child, - snippet->class_name); + matched_snippet->class_name); lasso_assign_new_gobject(*(LassoNode**)value, subnode); } break; @@ -1603,8 +1663,8 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) case SNIPPET_CONTENT: case SNIPPET_LIST_CONTENT: content = xmlNodeGetContent(t); - if (is_snippet_type(snippet, SNIPPET_CONTENT)) { - snippet_set_value(node, class, snippet, content); + if (is_snippet_type(matched_snippet, SNIPPET_CONTENT)) { + snippet_set_value(node, class, matched_snippet, content); } else { /* only list of string-like xsd:type supported */ lasso_list_add_string(*list, (char*)content); } @@ -1620,22 +1680,9 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) } /* When creating a new LassoNode and option KEEP_XMLNODE is present, * we attached the xmlNode to the LassoNode */ - if (subnode && (snippet->type & SNIPPET_KEEP_XMLNODE)) { + if (subnode && (matched_snippet->type & SNIPPET_KEEP_XMLNODE)) { lasso_node_set_original_xmlnode(subnode, t); } - switch (snippet->type & 0xff) { - case SNIPPET_NODE: - case SNIPPET_NODE_IN_CHILD: - case SNIPPET_XMLNODE: - case SNIPPET_CONTENT: - case SNIPPET_SIGNATURE: - /* Only one node to read, advance ! */ - ++snippet; - next_node_snippet(&class_iter, &snippet); - break; - default: - break; - } } else { g_assert_not_reached(); } @@ -1681,7 +1728,8 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) "class %s", t->ns ? t->ns->href : NULL, t->name, g_type_name(G_TYPE_FROM_INSTANCE(node))); - return 1; + rc = 1; + goto cleanup; } } } @@ -1735,8 +1783,9 @@ lasso_node_impl_init_from_xml(LassoNode *node, xmlNode *xmlnode) lasso_release_xml_string(private_key_password); lasso_release_xml_string(certificate); } - - return 0; +cleanup: + lasso_release_slist(class_list); + return rc; } #undef trace_snippet @@ -2789,6 +2838,7 @@ lasso_node_build_xmlNode_from_snippets(LassoNode *node, LassoNodeClass *class, x case SNIPPET_ANY: case SNIPPET_KEEP_XMLNODE: case SNIPPET_PRIVATE: + case SNIPPET_MANDATORY: case SNIPPET_UNUSED1: g_assert_not_reached(); } diff --git a/tests/basic_tests.c b/tests/basic_tests.c index 78c70d7b..27218177 100644 --- a/tests/basic_tests.c +++ b/tests/basic_tests.c @@ -137,6 +137,22 @@ START_TEST(test07_registry_functional_mapping) } END_TEST +static struct XmlSnippet schema_snippets[] = { + {NULL, 0, 0, NULL, NULL, NULL} +}; + +static void +class_init(LassoNodeClass *klass) +{ + LassoNodeClass *nclass = LASSO_NODE_CLASS(klass); + + nclass->node_data = g_new0(LassoNodeClassData, 1); + lasso_node_class_set_nodename(nclass, "Assertion"); + lasso_node_class_set_ns(nclass,LASSO_SAML2_ASSERTION_HREF, LASSO_SAML2_ASSERTION_PREFIX); + lasso_node_class_add_snippets(nclass, schema_snippets); + +} + START_TEST(test08_test_new_from_xmlNode) { static GType this_type = 0; @@ -147,7 +163,7 @@ START_TEST(test08_test_new_from_xmlNode) sizeof (LassoNodeClass), NULL, NULL, - NULL, + (GClassInitFunc) class_init, NULL, NULL, sizeof(LassoNode), @@ -1495,24 +1511,24 @@ START_TEST(test10_test_alldumps) lasso_release_string(node_dump); lasso_release_gobject(node2); lasso_release_gobject(node); - node = LASSO_NODE(lasso_samlp2_logout_request_new()); +/* node = LASSO_NODE(lasso_samlp2_logout_request_new()); node_dump = lasso_node_dump(node); fail_unless((node2 = lasso_node_new_from_dump(node_dump)) != NULL, "restoring dump failed after lasso_samlp2_logout_request_new"); lasso_release_string(node_dump); lasso_release_gobject(node2); - lasso_release_gobject(node); + lasso_release_gobject(node); */ node = LASSO_NODE(lasso_samlp2_logout_response_new()); node_dump = lasso_node_dump(node); fail_unless((node2 = lasso_node_new_from_dump(node_dump)) != NULL, "restoring dump failed after lasso_samlp2_logout_response_new"); lasso_release_string(node_dump); lasso_release_gobject(node2); lasso_release_gobject(node); - node = LASSO_NODE(lasso_samlp2_manage_name_id_request_new()); +/* node = LASSO_NODE(lasso_samlp2_manage_name_id_request_new()); node_dump = lasso_node_dump(node); fail_unless((node2 = lasso_node_new_from_dump(node_dump)) != NULL, "restoring dump failed after lasso_samlp2_manage_name_id_request_new"); lasso_release_string(node_dump); lasso_release_gobject(node2); - lasso_release_gobject(node); + lasso_release_gobject(node); */ node = LASSO_NODE(lasso_samlp2_manage_name_id_response_new()); node_dump = lasso_node_dump(node); fail_unless((node2 = lasso_node_new_from_dump(node_dump)) != NULL, "restoring dump failed after lasso_samlp2_manage_name_id_response_new"); diff --git a/tools/git-version-gen b/tools/git-version-gen new file mode 100755 index 00000000..34682473 --- /dev/null +++ b/tools/git-version-gen @@ -0,0 +1,225 @@ +#!/bin/sh +# Print a version string. +scriptversion=2012-12-31.23; # UTC + +# Copyright (C) 2007-2013 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# As with any generated file in a VC'd directory, you should add +# /.version to .gitignore, so that you don't accidentally commit it. +# .tarball-version is never generated in a VC'd directory, so needn't +# be listed there. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .version and +# .tarball-version will exist in distribution tarballs. +# +# EXTRA_DIST = $(top_srcdir)/.version +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + + +me=$0 + +version="git-version-gen $scriptversion + +Copyright 2011 Free Software Foundation, Inc. +There is NO warranty. You may redistribute this software +under the terms of the GNU General Public License. +For more information about these matters, see the files named COPYING." + +usage="\ +Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT] +Print a version string. + +Options: + + --prefix prefix of git tags (default 'v') + --fallback fallback version to use if \"git --version\" fails + + --help display this help and exit + --version output version information and exit + +Running without arguments will suffice in most cases." + +prefix=v +fallback= + +while test $# -gt 0; do + case $1 in + --help) echo "$usage"; exit 0;; + --version) echo "$version"; exit 0;; + --prefix) shift; prefix="$1";; + --fallback) shift; fallback="$1";; + -*) + echo "$0: Unknown option '$1'." >&2 + echo "$0: Try '--help' for more information." >&2 + exit 1;; + *) + if test "x$tarball_version_file" = x; then + tarball_version_file="$1" + elif test "x$tag_sed_script" = x; then + tag_sed_script="$1" + else + echo "$0: extra non-option argument '$1'." >&2 + exit 1 + fi;; + esac + shift +done + +if test "x$tarball_version_file" = x; then + echo "$usage" + exit 1 +fi + +tag_sed_script="${tag_sed_script:-s/x/x/}" + +nl=' +' + +# Avoid meddling by environment variable of the same name. +v= +v_from_git= + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || v= + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test "x$v" = x \ + && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2 +fi + +if test "x$v" != x +then + : # use $v +# Otherwise, if there is at least one git commit involving the working +# directory, and "git describe" output looks sensible, use that to +# derive a version string. +elif test "`git log -1 --pretty=format:x . 2>&1`" = x \ + && v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \ + && case $v in + $prefix[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \ + || { commit_list=failed; + echo "$0: WARNING: git rev-list failed" 1>&2; } + numcommits=`echo "$commit_list" | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + test "$commit_list" = failed && v=UNKNOWN + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; + v_from_git=1 +elif test "x$fallback" = x || git --version >/dev/null 2>&1; then + v=UNKNOWN +else + v=$fallback +fi + +v=`echo "$v" |sed "s/^$prefix//"` + +# Test whether to append the "-dirty" suffix only if the version +# string we're using came from git. I.e., skip the test if it's "UNKNOWN" +# or if it came from .tarball-version. +if test "x$v_from_git" != x; then + # Don't declare a version "dirty" merely because a time stamp has changed. + git update-index --refresh > /dev/null 2>&1 + + dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty= + case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; + esac +fi + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d "$nl" + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/website/web/index.xml b/website/web/index.xml index 73059b9c..dab4af1f 100644 --- a/website/web/index.xml +++ b/website/web/index.xml @@ -22,7 +22,7 @@ We strongly recommend the use of the <a href="/license">GNU General Public License</a> each time it is possible. But for proprietary projects, that wouldn't want to use it, we designed a <a - href="http://www.entrouvert.com/en/digital-identity/license-and-support">commercial + href="http://www.entrouvert.com/en/expertise/licenses/">commercial license</a>. </p> |