From fb292eaa6624eaad6f536eafa9d799e1b18f5ccb Mon Sep 17 00:00:00 2001
From: Andrea Zagli <azagli@libero.it>
Date: Mon, 5 Jul 2010 19:34:08 +0200
Subject: [PATCH] Added functions Autoz::add_parent_to_role,
 Autoz::add_parents_to_role, Autoz::add_parent_to_resource,
 Autoz::add_parents_to_resource and Autoz::load_from_xml (but there is yet
 some problems). Bugfixes.

---
 .gitignore                            |   1 +
 docs/reference/autoz-undeclared.txt   |   0
 docs/reference/autoz-undocumented.txt |  47 ----
 docs/reference/autoz-unused.txt       |   0
 docs/reference/autoz.args             |   0
 docs/reference/autoz.hierarchy        |   8 -
 docs/reference/autoz.interfaces       |   2 -
 docs/reference/autoz.prerequisites    |   2 -
 docs/reference/autoz.signals          |   0
 src/autoz.c                           | 312 ++++++++++++++++++++++++--
 src/autoz.h                           |   5 +
 tests/Makefile.am                     |   5 +-
 tests/test_from_xml.c                 |  79 +++++++
 tests/test_from_xml.xml               |  17 ++
 14 files changed, 396 insertions(+), 82 deletions(-)
 delete mode 100644 docs/reference/autoz-undeclared.txt
 delete mode 100644 docs/reference/autoz-undocumented.txt
 delete mode 100644 docs/reference/autoz-unused.txt
 delete mode 100644 docs/reference/autoz.args
 delete mode 100644 docs/reference/autoz.hierarchy
 delete mode 100644 docs/reference/autoz.interfaces
 delete mode 100644 docs/reference/autoz.prerequisites
 delete mode 100644 docs/reference/autoz.signals
 create mode 100644 tests/test_from_xml.c
 create mode 100644 tests/test_from_xml.xml

diff --git a/.gitignore b/.gitignore
index f9812ae..2d297f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,7 @@ docs/reference/xml/
 libtool
 stamp-h1
 tests/test
+tests/test_from_xml
 POTFILES
 mkinstalldirs
 stamp-it
diff --git a/docs/reference/autoz-undeclared.txt b/docs/reference/autoz-undeclared.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/reference/autoz-undocumented.txt b/docs/reference/autoz-undocumented.txt
deleted file mode 100644
index 9b689e4..0000000
--- a/docs/reference/autoz-undocumented.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-2% symbol docs coverage.
-1 symbols documented.
-5 symbols incomplete.
-39 not documented.
-
-
-AUTOZ
-AUTOZ_CLASS
-AUTOZ_GET_CLASS
-Autoz
-AutozClass (<items>)
-AutozIResource
-AutozIResourceIface (<items>)
-AutozIRole
-AutozIRoleIface (<items>)
-AutozResource
-AutozResourceClass (<items>)
-AutozRole
-AutozRoleClass (<items>)
-IS_AUTOZ
-IS_AUTOZ_CLASS
-TYPE_AUTOZ
-autoz_add_resource
-autoz_add_resource_with_parents
-autoz_add_role
-autoz_add_role_with_parents
-autoz_allow
-autoz_get_resource_from_id
-autoz_get_role_from_id
-autoz_get_type
-autoz_iresource_get_resource_id
-autoz_irole_get_role_id
-autoz_is_allowed
-autoz_new
-autoz_resource_new
-autoz_role_new
-
-
-autoz:Short_Description
-resource:Long_Description
-resource:Short_Description
-resource_interface:Long_Description
-resource_interface:Short_Description
-role:Long_Description
-role:Short_Description
-role_interface:Long_Description
-role_interface:Short_Description
diff --git a/docs/reference/autoz-unused.txt b/docs/reference/autoz-unused.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/reference/autoz.args b/docs/reference/autoz.args
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/reference/autoz.hierarchy b/docs/reference/autoz.hierarchy
deleted file mode 100644
index c92c2ab..0000000
--- a/docs/reference/autoz.hierarchy
+++ /dev/null
@@ -1,8 +0,0 @@
-GObject
-  Autoz
-  AutozResource
-  AutozRole
-GInterface
-  GTypePlugin
-  AutozIResource
-  AutozIRole
diff --git a/docs/reference/autoz.interfaces b/docs/reference/autoz.interfaces
deleted file mode 100644
index 642b295..0000000
--- a/docs/reference/autoz.interfaces
+++ /dev/null
@@ -1,2 +0,0 @@
-AutozResource AutozIResource
-AutozRole AutozIRole
diff --git a/docs/reference/autoz.prerequisites b/docs/reference/autoz.prerequisites
deleted file mode 100644
index 8b67f96..0000000
--- a/docs/reference/autoz.prerequisites
+++ /dev/null
@@ -1,2 +0,0 @@
-AutozIResource GObject
-AutozIRole GObject
diff --git a/docs/reference/autoz.signals b/docs/reference/autoz.signals
deleted file mode 100644
index e69de29..0000000
diff --git a/src/autoz.c b/src/autoz.c
index 725981d..13daaca 100644
--- a/src/autoz.c
+++ b/src/autoz.c
@@ -24,6 +24,9 @@
 
 #include "autoz.h"
 
+#include "role.h"
+#include "resource.h"
+
 typedef struct _Role Role;
 struct _Role
 	{
@@ -128,6 +131,7 @@ autoz_add_role_with_parents (Autoz *autoz, AutozIRole *irole, ...)
 	AutozPrivate *priv = AUTOZ_GET_PRIVATE (autoz);
 
 	const gchar *role_id;
+	const gchar *role_id_parent;
 
 	g_return_if_fail (IS_AUTOZ (autoz));
 	g_return_if_fail (AUTOZ_IS_IROLE (irole));
@@ -149,16 +153,92 @@ autoz_add_role_with_parents (Autoz *autoz, AutozIRole *irole, ...)
 			va_start (args, irole);
 			while ((irole_parent = va_arg (args, AutozIRole *)) != NULL)
 				{
-					role_parent = g_hash_table_lookup (priv->roles, autoz_irole_get_role_id (irole_parent));
-					if (role_parent != NULL)
+					role_id_parent = autoz_irole_get_role_id (irole_parent);
+					if (g_strcmp0 (role_id, role_id_parent) == 0)
 						{
-							role->parents = g_list_append (role->parents, role_parent);
-						}					
+							g_warning ("The parent cannot be himself (%s).", role_id);
+						}
+					else
+						{					
+							role_parent = g_hash_table_lookup (priv->roles, role_id_parent);
+							if (role_parent != NULL)
+								{
+									role->parents = g_list_append (role->parents, role_parent);
+								}
+							else
+								{
+									g_warning ("Role «%s» not found.", autoz_irole_get_role_id (irole_parent));
+								}
+						}
 				}
 			va_end (args);
 
 			g_hash_table_insert (priv->roles, (gpointer)role_id, (gpointer)role);
 		}
+	else
+		{
+			g_warning ("Role «%s» not found.", role_id);
+		}
+}
+
+void
+autoz_add_parent_to_role (Autoz *autoz, AutozIRole *irole, AutozIRole *irole_parent)
+{
+	autoz_add_parents_to_role (autoz, irole, irole_parent, NULL);
+}
+
+void
+autoz_add_parents_to_role (Autoz *autoz, AutozIRole *irole, ...)
+{
+	AutozPrivate *priv;
+
+	Role *role;
+
+	const gchar *role_id;
+	const gchar *role_id_parent;
+
+	g_return_if_fail (IS_AUTOZ (autoz));
+	g_return_if_fail (AUTOZ_IS_IROLE (irole));
+
+	priv = AUTOZ_GET_PRIVATE (autoz);
+
+	role_id = autoz_irole_get_role_id (irole);
+
+	role = g_hash_table_lookup (priv->roles, role_id);
+	if (role != NULL)
+		{
+			va_list args;
+
+			AutozIRole *irole_parent;
+			Role *role_parent;
+
+			va_start (args, irole);
+			while ((irole_parent = va_arg (args, AutozIRole *)) != NULL)
+				{
+					role_id_parent = autoz_irole_get_role_id (irole_parent);
+					if (g_strcmp0 (role_id, role_id_parent) == 0)
+						{
+							g_warning ("The parent cannot be himself (%s).", role_id);
+						}
+					else
+						{
+							role_parent = g_hash_table_lookup (priv->roles, role_id_parent);
+							if (role_parent != NULL)
+								{
+									role->parents = g_list_append (role->parents, role_parent);
+								}
+							else
+								{
+									g_warning ("Role «%s» not found.", role_id);
+								}
+						}
+				}
+			va_end (args);
+		}
+	else
+		{
+			g_warning ("Role «%s» not found.", role_id);
+		}
 }
 
 AutozIRole
@@ -184,64 +264,123 @@ AutozIRole
 
 void
 autoz_add_resource (Autoz *autoz, AutozIResource *iresource)
+{
+	autoz_add_resource_with_parents (autoz, iresource, NULL);
+}
+
+void
+autoz_add_resource_with_parents (Autoz *autoz, AutozIResource *iresource, ...)
 {
 	AutozPrivate *priv = AUTOZ_GET_PRIVATE (autoz);
 
+	const gchar *resource_id;
+	const gchar *resource_id_parent;
+
 	g_return_if_fail (IS_AUTOZ (autoz));
 	g_return_if_fail (AUTOZ_IS_IRESOURCE (iresource));
 
-	const gchar *resource_id;
-
 	resource_id = autoz_iresource_get_resource_id (iresource);
 
 	if (g_hash_table_lookup (priv->resources, resource_id) == NULL)
 		{
+			va_list args;
 			Resource *resource;
 
+			AutozIResource *iresource_parent;
+			Resource *resource_parent;
+
 			resource = (Resource *)g_malloc0 (sizeof (Resource));
 			resource->iresource = iresource;
 			resource->parents = NULL;
 
+			va_start (args, iresource);
+			while ((iresource_parent = va_arg (args, AutozIResource *)) != NULL)
+				{
+					resource_id_parent = autoz_iresource_get_resource_id (iresource_parent);
+					if (g_strcmp0 (resource_id, resource_id_parent) == 0)
+						{
+							g_warning ("The parent cannot be himself (%s).", resource_id);
+						}
+					else
+						{
+							resource_parent = g_hash_table_lookup (priv->resources, resource_id_parent);
+							if (resource_parent != NULL)
+								{
+									resource->parents = g_list_append (resource->parents, resource_parent);
+								}					
+							else
+								{
+									g_warning ("Resource «%s» not found.", autoz_iresource_get_resource_id (iresource_parent));
+							}
+						}	
+				}
+			va_end (args);
+
 			g_hash_table_insert (priv->resources, (gpointer)resource_id, (gpointer)resource);
 		}
+	else
+		{
+			g_warning ("Resource «%s» not found.", resource_id);
+		}
 }
 
 void
-autoz_add_resource_with_parents (Autoz *autoz, AutozIResource *iresource, ...)
+autoz_add_parent_to_resource (Autoz *autoz, AutozIResource *iresource, AutozIResource *iresource_parent)
 {
-	AutozPrivate *priv = AUTOZ_GET_PRIVATE (autoz);
+	autoz_add_parents_to_resource (autoz, iresource, iresource_parent, NULL);
+}
+
+void
+autoz_add_parents_to_resource (Autoz *autoz, AutozIResource *iresource, ...)
+{
+	AutozPrivate *priv;
+
+	Resource *resource;
 
 	const gchar *resource_id;
+	const gchar *resource_id_parent;
 
 	g_return_if_fail (IS_AUTOZ (autoz));
 	g_return_if_fail (AUTOZ_IS_IRESOURCE (iresource));
 
+	priv = AUTOZ_GET_PRIVATE (autoz);
+
 	resource_id = autoz_iresource_get_resource_id (iresource);
 
-	if (g_hash_table_lookup (priv->resources, resource_id) == NULL)
+	resource = g_hash_table_lookup (priv->resources, resource_id);
+	if (resource != NULL)
 		{
 			va_list args;
-			Resource *resource;
 
 			AutozIResource *iresource_parent;
 			Resource *resource_parent;
 
-			resource = (Resource *)g_malloc0 (sizeof (Resource));
-			resource->iresource = iresource;
-			resource->parents = NULL;
-
 			va_start (args, iresource);
 			while ((iresource_parent = va_arg (args, AutozIResource *)) != NULL)
 				{
-					resource_parent = g_hash_table_lookup (priv->resources, autoz_iresource_get_resource_id (iresource_parent));
-					if (resource_parent != NULL)
+					resource_id_parent = autoz_iresource_get_resource_id (iresource_parent);
+					if (g_strcmp0 (resource_id, resource_id_parent) == 0)
 						{
-							resource->parents = g_list_append (resource->parents, resource_parent);
-						}					
+							g_warning ("The parent cannot be himself (%s).", resource_id);
+						}
+					else
+						{
+							resource_parent = g_hash_table_lookup (priv->roles, resource_id_parent);
+							if (resource_parent != NULL)
+								{
+									resource->parents = g_list_append (resource->parents, resource_parent);
+								}
+							else
+								{
+									g_warning ("Resource «%s» not found.", resource_id);
+								}
+						}
 				}
 			va_end (args);
-
-			g_hash_table_insert (priv->resources, (gpointer)resource_id, (gpointer)resource);
+		}
+	else
+		{
+			g_warning ("Resource «%s» not found.", resource_id);
 		}
 }
 
@@ -714,7 +853,7 @@ autoz_get_xml (Autoz *autoz)
 				}
 			else
 				{
-					xmlSetProp (xnode, "resource", "all");
+					xmlSetProp (xnode, "resource", "");
 				}
 
 			xmlAddChild (ret, xnode);
@@ -745,6 +884,137 @@ autoz_get_xml (Autoz *autoz)
 	return ret;
 }
 
+gboolean
+autoz_load_from_xml (Autoz *autoz, xmlNodePtr xnode, gboolean replace)
+{
+	gboolean ret;
+
+	AutozPrivate *priv;
+
+	xmlNodePtr current;
+	xmlNodePtr current_parent;
+
+	AutozIRole *irole;
+	AutozIResource *iresource;
+	gchar *prop;
+
+	g_return_val_if_fail (IS_AUTOZ (autoz), FALSE);
+	g_return_val_if_fail (xnode != NULL, FALSE);
+
+	priv = AUTOZ_GET_PRIVATE (autoz);
+
+	ret = TRUE;
+
+	if (replace)
+		{
+			/* clearing current authorizations */
+			g_hash_table_destroy (priv->roles);
+			g_hash_table_destroy (priv->resources);
+			g_hash_table_destroy (priv->rules_allow);
+			g_hash_table_destroy (priv->rules_deny);
+
+			priv->roles = g_hash_table_new (g_str_hash, g_str_equal);
+			priv->resources = g_hash_table_new (g_str_hash, g_str_equal);
+			priv->rules_allow = g_hash_table_new (g_str_hash, g_str_equal);
+			priv->rules_deny = g_hash_table_new (g_str_hash, g_str_equal);
+		}
+
+	if (xmlStrcmp (xnode->name, "autoz") != 0)
+		{
+			g_warning ("Invalid xml structure.");
+			ret = FALSE;
+		}
+	else
+		{
+			current = xnode->children;
+			while (current != NULL)
+				{
+					if (!xmlNodeIsText (current))
+						{
+							if (xmlStrcmp (current->name, "role") == 0)
+								{
+									prop = g_strstrip (g_strdup ((gchar *)xmlGetProp (current, "id")));
+									if (g_strcmp0 (prop, "") != 0)
+										{
+											irole = AUTOZ_IROLE (autoz_role_new (prop));
+											autoz_add_role (autoz, irole);
+
+											current_parent = current->children;
+											while (current_parent != NULL)
+												{
+													if (!xmlNodeIsText (current_parent) &&
+													    xmlStrcmp (current_parent->name, "parent") == 0)
+													    {
+													    	prop = g_strstrip (g_strdup ((gchar *)xmlGetProp (current_parent, "id")));
+													    	if (g_strcmp0 (prop, "") != 0)
+																{
+																	autoz_add_parent_to_role (autoz, irole,  autoz_get_role_from_id (autoz, prop));
+																}
+													    }
+													current_parent = current_parent->next;
+												}
+										}
+								}
+							else if (xmlStrcmp (current->name, "resource") == 0)
+								{
+									prop = g_strstrip (g_strdup ((gchar *)xmlGetProp (current, "id")));
+									if (g_strcmp0 (prop, "") != 0)
+										{
+											iresource = AUTOZ_IRESOURCE (autoz_resource_new (prop));
+											autoz_add_resource (autoz, iresource);
+
+											current_parent = current->children;
+											while (current_parent != NULL)
+												{
+													if (!xmlNodeIsText (current_parent) &&
+													    xmlStrcmp (current_parent->name, "parent") == 0)
+													    {
+													    	prop = g_strstrip (g_strdup ((gchar *)xmlGetProp (current_parent, "id")));
+													    	if (g_strcmp0 (prop, "") != 0)
+																{
+																	autoz_add_parent_to_resource (autoz, iresource, autoz_get_resource_from_id (autoz, prop));
+																}
+													    }
+													current_parent = current_parent->next;
+												}
+										}
+								}
+							else if (xmlStrcmp (current->name, "rule") == 0)
+								{
+									prop = g_strstrip (g_strdup ((gchar *)xmlGetProp (current, "role")));
+									irole = autoz_get_role_from_id (autoz, prop);
+									if (irole != NULL)
+										{
+											prop = g_strstrip (g_strdup ((gchar *)xmlGetProp (current, "resource")));
+											if (g_strcmp0 (prop, "") == 0)
+												{
+													iresource = NULL;
+												}
+											else
+												{
+													iresource = autoz_get_resource_from_id (autoz, prop);
+												}
+
+											prop = g_strstrip (g_strdup ((gchar *)xmlGetProp (current, "allow")));
+											if (g_strcmp0 (prop, "yes") == 0)
+												{
+													autoz_allow (autoz, irole, iresource);
+												}
+											else
+												{
+													autoz_deny (autoz, irole, iresource);
+												}
+										}
+								}
+						}
+
+					current = current->next;
+				}
+		}
+
+	return ret;
+}
+
 /* PRIVATE */
 static void
 autoz_set_property (GObject *object,
diff --git a/src/autoz.h b/src/autoz.h
index 3112d1c..9069f3b 100644
--- a/src/autoz.h
+++ b/src/autoz.h
@@ -59,11 +59,15 @@ Autoz *autoz_new (void);
 
 void autoz_add_role (Autoz *autoz, AutozIRole *irole);
 void autoz_add_role_with_parents (Autoz *autoz, AutozIRole *irole, ...);
+void autoz_add_parent_to_role (Autoz *autoz, AutozIRole *irole, AutozIRole *irole_parent);
+void autoz_add_parents_to_role (Autoz *autoz, AutozIRole *irole, ...);
 
 AutozIRole *autoz_get_role_from_id (Autoz *autoz, const gchar *role_id);
 
 void autoz_add_resource (Autoz *autoz, AutozIResource *iresource);
 void autoz_add_resource_with_parents (Autoz *autoz, AutozIResource *iresource, ...);
+void autoz_add_parent_to_resource (Autoz *autoz, AutozIResource *iresource, AutozIResource *iresource_parent);
+void autoz_add_parents_to_resource (Autoz *autoz, AutozIResource *iresource, ...);
 
 AutozIResource *autoz_get_resource_from_id (Autoz *autoz, const gchar *resource_id);
 
@@ -73,6 +77,7 @@ void autoz_deny (Autoz *autoz, AutozIRole *irole, AutozIResource *iresource);
 gboolean autoz_is_allowed (Autoz *autoz, AutozIRole *irole, AutozIResource *iresource);
 
 xmlNodePtr autoz_get_xml (Autoz *autoz);
+gboolean autoz_load_fro_xml (Autoz *autoz, xmlNodePtr xnode, gboolean replace);
 
 
 G_END_DECLS
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7345b40..f64b798 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,8 +5,9 @@ AM_CPPFLAGS = $(AUTOZ_CFLAGS) \
               -I$(top_srcdir)/src \
               -DGUIDIR="\"@abs_builddir@\""
 
-noinst_PROGRAMS = test
+noinst_PROGRAMS = test \
+                  test_from_xml
 
 LDADD = $(top_builddir)/src/libautoz.la
 
-EXTRA_DIST = 
+EXTRA_DIST = test_from_xml.xml
diff --git a/tests/test_from_xml.c b/tests/test_from_xml.c
new file mode 100644
index 0000000..4f3883d
--- /dev/null
+++ b/tests/test_from_xml.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 Andrea Zagli <azagli@libero.it>
+ *
+ *  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 2 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <libxml/tree.h>
+
+#include "autoz.h"
+
+int
+main (int argc, char **argv)
+{
+	Autoz *autoz;
+
+	xmlDocPtr xdoc;
+	xmlNodePtr xnode;
+
+	g_type_init ();
+
+	autoz = autoz_new ();
+
+	if (argc == 0)
+		{
+			g_error ("You must specified an xml file to load.");
+			return 0;
+		}
+
+	xdoc = xmlParseFile (argv[1]);
+	if (xdoc == NULL)
+		{
+			g_error ("Unable to parse xml file.");
+			return 0;
+		}
+
+	autoz_load_from_xml (autoz, xmlDocGetRootElement (xdoc));
+
+	/* get xml */
+	xnode = autoz_get_xml (autoz);
+	if (xnode != NULL)
+		{
+			xdoc = xmlNewDoc ("1.0");
+			xmlDocSetRootElement (xdoc, xnode);
+			g_fprintf (stdout, "\n");
+			xmlSaveFormatFile ("-", xdoc, 2);
+			g_fprintf (stdout, "\n");
+		}
+
+	g_message ("super-admin %s allowed to page.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "super-admin"), autoz_get_resource_from_id (autoz, "page")) ? "is" : "isn't"));
+	g_message ("super-admin %s allowed to paragraph.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "super-admin"), autoz_get_resource_from_id (autoz, "paragraph")) ? "is" : "isn't"));
+	g_message ("writer %s allowed to page.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "writer"), autoz_get_resource_from_id (autoz, "page")) ? "is" : "isn't"));
+	g_message ("writer-child %s allowed to page.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "writer-child"), autoz_get_resource_from_id (autoz, "page")) ? "is" : "isn't"));
+	g_message ("writer %s allowed to paragraph.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "writer"), autoz_get_resource_from_id (autoz, "paragraph")) ? "is" : "isn't"));
+	g_message ("writer-child %s allowed to paragraph.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "writer-child"), autoz_get_resource_from_id (autoz, "paragraph")) ? "is" : "isn't"));
+	g_message ("read-only %s allowed to page.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "read-only"), autoz_get_resource_from_id (autoz, "page")) ? "is" : "isn't"));
+	g_message ("read-only %s allowed to paragraph.",
+	           (autoz_is_allowed (autoz, autoz_get_role_from_id (autoz, "read-only"), autoz_get_resource_from_id (autoz, "paragraph")) ? "is" : "isn't"));
+
+	return 0;
+}
diff --git a/tests/test_from_xml.xml b/tests/test_from_xml.xml
new file mode 100644
index 0000000..856e50e
--- /dev/null
+++ b/tests/test_from_xml.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<autoz>
+	<role id="read-only"/>
+	<role id="super-admin"/>
+	<role id="writer"/>
+	<role id="writer-child">
+		<parent id="writer"/>
+	</role>
+	<resource id="page"/>
+	<resource id="paragraph">
+		<parent id="page"/>
+	</resource>
+	<rule allow="yes" role="writer" resource="page"/>
+	<rule allow="yes" role="super-admin" resource=""/>
+	<rule allow="no" role="writer-child" resource="paragraph"/>
+</autoz>
+
-- 
2.49.0