Hi,

Sorry for delaying this update so long because of some other works.

The ACL implementation has been improved. As we discussed, two new
functionalities has been added:
* The access control on attributes of elements
* xpath based ACL.

The schema and the corresponding codes has been simplified:

  <define name="element-acls">
    <element name="acls">
      <zeroOrMore>
        <choice>
          <element name="user">
            <attribute name="id"><text/></attribute>
            <choice>
              <attribute name="role"><data type="IDREF"/></attribute>
              <zeroOrMore>
                <ref name="element-acl"/>
              </zeroOrMore>
            </choice>
          </element>
          <element name="role">
            <attribute name="id"><data type="ID"/></attribute>
            <zeroOrMore>
              <ref name="element-acl"/>
            </zeroOrMore>
          </element>
        </choice>
      </zeroOrMore>
    </element>
  </define>

  <define name="element-acl">
    <choice>
      <element name="read">
        <ref name="attribute-acl"/>
      </element>
      <element name="write">
        <ref name="attribute-acl"/>
      </element>
      <element name="deny">
        <ref name="attribute-acl"/>
      </element>
    </choice>
  </define>

  <define name="attribute-acl">
    <attribute name="id"><data type="ID"/></attribute>
      <choice>
        <attribute name="tag"><text/></attribute>
        <attribute name="ref"><data type="IDREF"/></attribute>
        <group>
          <attribute name="tag"><text/></attribute>
          <attribute name="ref"><data type="IDREF"/></attribute>
        </group>
        <attribute name="xpath"><text/></attribute>
      </choice>
      <optional>
        <attribute name="attribute"><text/></attribute>
      </optional>
  </define>


A configuration example:
..
<acls>
  <role id="operator">
    <write id="operator-write-0" tag="nodes"/>
    <write id="operator-write-1" tag="status"/>
  </role>
  <role id="monitor">
    <read id="monitor-read-0" tag="nodes"/>
    <read id="monitor-read-1" tag="status"/>
  </role>
  <user id="gaoyan">
    <write id="gaoyan-write-0" 
xpath="//primiti...@id='rsc0']//nvpa...@name='target-role']"/>
    <deny id ="gaoyan-deny-0" 
xpath="//primiti...@id='rsc0']//nvpa...@name='password']" attribute="value"/>
    <read id="gaoyan-read-0" tag="primitive" ref="rsc0"/>
    <write id="gaoyan-write-1 ref="location_rsc0"/>
    <write id="gaoyan-write-2" tag="nodes"/>
    <write id="gaoyan-write-3" tag="status"/>
  </user>
  <user id="bob" role="operator"/>
  <user id="1002" role="monitor"/>
</acls>
..

As Andrew suggested:
- Roles have ACLs
- Users can be assigned EITHER a role OR a set of ACLs

Besides, An user "id" could be a system username or a numeric uid.

For crm shell, perhaps the syntax would be like:

user <id> acl_obj [acl_obj ...]
user <id> <role_id>
role <id> acl_obj [acl_obj ...]

acl_obj ::
  mode tag <tag_name> [attribute]
  mode ref <ref_id> [attribute]
  mode tag <tag_name> ref <ref_id> [attribute]
  mode xpath <path> [attribute]

mode:: read | write | deny

Attached the updated patch. Please help review it.
Thanks!

Regards,
  Yan
-- 
Yan Gao <y...@novell.com>
Software Engineer
China Server Team, OPS Engineering, Novell, Inc.



# HG changeset patch
# User Yan Gao <y...@novell.com>
# Date 1265036078 -28800
# Node ID 7fecd951b21c6d820654e6115a4d96e839641cda
# Parent  ae2f826609edfedb37aeb4b997171f55bb509352
Dev: cib: Implement multi-level ACLs for the CIB

diff -r ae2f826609ed -r 7fecd951b21c cib/callbacks.c
--- a/cib/callbacks.c	Mon Jan 25 10:54:01 2010 +0100
+++ b/cib/callbacks.c	Mon Feb 01 22:54:38 2010 +0800
@@ -298,8 +298,16 @@
 		    }
 		}
 
+		if(cib_client->user == NULL) {
+		    value = crm_element_value(op_request, F_CIB_USER);
+		    if(value != NULL) {
+			cib_client->user = crm_strdup(value);
+		    }
+		}
+
 		crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id);
 		crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name);
+		crm_xml_add(op_request, F_CIB_USER, cib_client->user);
 		/* crm_log_xml(LOG_MSG, "Client[inbound]", op_request); */
 
 		if(cib_client->callback_id == NULL) {
@@ -797,6 +805,7 @@
     xmlNode *output      = NULL;
     xmlNode *result_cib  = NULL;
     xmlNode *current_cib = NULL;
+    xmlNode *filtered_current_cib = NULL;
 	
     int call_type    = 0;
     int call_options = 0;
@@ -838,9 +847,20 @@
 	goto done;
 		
     } else if(cib_op_modifies(call_type) == FALSE) {
-	rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE,
+	if (acl_filter_cib(request, current_cib, current_cib, &filtered_current_cib) == FALSE) {
+	    rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE,
 			    section, request, input, FALSE, &config_changed,
 			    current_cib, &result_cib, NULL, &output);
+	} else {
+	    crm_debug("Pre-filtered the queried cib according to the ACLs");
+	    if (filtered_current_cib == NULL) {
+		rc = cib_permission_denied;
+	    } else {
+		rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE,
+				section, request, input, FALSE, &config_changed,
+				filtered_current_cib, &result_cib, NULL, &output);
+	    }
+	}
 
 	CRM_CHECK(result_cib == NULL, free_xml(result_cib));
 	goto done;
@@ -877,6 +897,10 @@
 	    *cib_diff = diff_cib_object(current_cib, result_cib, FALSE);
 	    config_changed = cib_config_changed(*cib_diff);
 	}
+
+	if (acl_check_diff(request, current_cib, result_cib, *cib_diff) == FALSE) {
+	    rc = cib_permission_denied;
+	}
     }    
     
     if(rc == cib_ok) {
@@ -901,11 +925,22 @@
 	}
 
     } else if(rc == cib_dtd_validation) {
+	xmlNode *filtered_result_cib = NULL;
+
 	if(output != NULL) {
 	    crm_log_xml_info(output, "cib:output");
 	    free_xml(output);
 	} 
-	output = result_cib;
+
+	if (acl_filter_cib(request, current_cib, result_cib, &filtered_result_cib) == FALSE) {
+	    output = result_cib;
+	} else {
+	    crm_debug("Filtered the result cib for output according to the ACLs");
+	    output = filtered_result_cib;
+	    if (result_cib != NULL) {
+		free_xml(result_cib);
+	    }
+	}
 
     } else {
 	free_xml(result_cib);    
@@ -949,6 +984,10 @@
     if((call_options & cib_discard_reply) == 0) {
 	*reply = cib_construct_reply(request, output, rc);
 	/* crm_log_xml_info(*reply, "cib:reply"); */
+    }
+
+    if (filtered_current_cib != NULL) {
+	free_xml(filtered_current_cib);
     }
 
     if(call_type >= 0) {
diff -r ae2f826609ed -r 7fecd951b21c cib/callbacks.h
--- a/cib/callbacks.h	Mon Jan 25 10:54:01 2010 +0100
+++ b/cib/callbacks.h	Mon Feb 01 22:54:38 2010 +0800
@@ -34,6 +34,7 @@
 		char  *id;
 		char  *name;
 		char  *callback_id;
+		char  *user;
 
 		const char  *channel_name;
 
diff -r ae2f826609ed -r 7fecd951b21c cib/remote.c
--- a/cib/remote.c	Mon Jan 25 10:54:01 2010 +0100
+++ b/cib/remote.c	Mon Feb 01 22:54:38 2010 +0800
@@ -289,6 +289,8 @@
 
 	CRM_CHECK(new_client->id == NULL, crm_free(new_client->id));
 	new_client->id = crm_strdup(uuid_str);
+
+	new_client->user = crm_strdup(user);
 	
 	new_client->callback_id = NULL;
 	if(ssock == remote_tls_fd) {
diff -r ae2f826609ed -r 7fecd951b21c include/crm/cib.h
--- a/include/crm/cib.h	Mon Jan 25 10:54:01 2010 +0100
+++ b/include/crm/cib.h	Mon Feb 01 22:54:38 2010 +0800
@@ -120,6 +120,7 @@
 	cib_bad_config		= -51,
 	cib_invalid_argument	= -52,
 	cib_transform_failed    = -53,
+	cib_permission_denied	= -54,
 };
 
 enum cib_update_op {
@@ -182,6 +183,7 @@
 #define F_CIB_NOTIFY_TYPE	"cib_notify_type"
 #define F_CIB_NOTIFY_ACTIVATE	"cib_notify_activate"
 #define F_CIB_UPDATE_DIFF	"cib_update_diff"
+#define F_CIB_USER		"cib_user"
 
 #define T_CIB			"cib"
 #define T_CIB_NOTIFY		"cib_notify"
diff -r ae2f826609ed -r 7fecd951b21c include/crm/msg_xml.h
--- a/include/crm/msg_xml.h	Mon Jan 25 10:54:01 2010 +0100
+++ b/include/crm/msg_xml.h	Mon Feb 01 22:54:38 2010 +0800
@@ -118,6 +118,7 @@
 #define XML_CIB_TAG_CRMCONFIG   	"crm_config"
 #define XML_CIB_TAG_OPCONFIG		"op_defaults"
 #define XML_CIB_TAG_RSCCONFIG   	"rsc_defaults"
+#define XML_CIB_TAG_ACLS   		"acls"
 
 #define XML_CIB_TAG_STATE         	"node_state"
 #define XML_CIB_TAG_NODE          	"node"
@@ -264,6 +265,17 @@
 #define XML_TAG_DIFF_ADDED		"diff-added"
 #define XML_TAG_DIFF_REMOVED		"diff-removed"
 
+#define XML_ACL_TAG_USER		"user"
+#define XML_ACL_TAG_ROLE		"role"
+#define XML_ACL_ATTR_ROLE		"role"
+#define XML_ACL_TAG_READ		"read"
+#define XML_ACL_TAG_WRITE		"write"
+#define XML_ACL_TAG_DENY		"deny"
+#define XML_ACL_ATTR_REF		"ref"
+#define XML_ACL_ATTR_TAG		"tag"
+#define XML_ACL_ATTR_XPATH		"xpath"
+#define XML_ACL_ATTR_ATTRIBUTE		"attribute"
+
 #include <crm/common/xml.h> 
 
 #define ID(x) crm_element_value(x, XML_ATTR_ID)
diff -r ae2f826609ed -r 7fecd951b21c lib/cib/Makefile.am
--- a/lib/cib/Makefile.am	Mon Jan 25 10:54:01 2010 +0100
+++ b/lib/cib/Makefile.am	Mon Feb 01 22:54:38 2010 +0800
@@ -26,7 +26,7 @@
 ## SOURCES
 noinst_HEADERS		= cib_private.h
 libcib_la_SOURCES	= cib_ops.c cib_utils.c cib_client.c cib_native.c cib_attrs.c \
-			cib_version.c cib_file.c cib_remote.c
+			cib_version.c cib_file.c cib_remote.c cib_acl.c
 
 libcib_la_LDFLAGS	= -version-info 1:1:0 $(top_builddir)/lib/common/libcrmcommon.la $(CRYPTOLIB)
 libcib_la_CFLAGS	= -I$(top_srcdir)
diff -r ae2f826609ed -r 7fecd951b21c lib/cib/cib_acl.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/cib/cib_acl.c	Mon Feb 01 22:54:38 2010 +0800
@@ -0,0 +1,727 @@
+/* 
+ * Copyright (C) 2009 Yan Gao <y...@novell.com>
+ * 
+ * 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.1 of the License, or (at your option) any later version.
+ * 
+ * This software 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <pwd.h>
+
+#include <crm/cib.h>
+#include <cib_private.h>
+
+typedef struct acl_obj_s
+{
+	const char *mode;
+	const char *tag;
+	const char *ref;
+	const char *xpath;
+	const char *attribute;
+} acl_obj_t;
+
+typedef struct xml_perm_s
+{
+	const char *mode;
+	GHashTable *attribute_perms;
+} xml_perm_t;
+
+static gboolean req_by_superuser(xmlNode *request);
+
+static gboolean unpack_user_acl(xmlNode *xml_acls, const char *user, GListPtr *user_acl);
+static gboolean user_match(const char *user, const char *uid);
+static gboolean unpack_acl(xmlNode *xml_acls, xmlNode *xml_acl, GListPtr *acl);
+static gboolean unpack_role_acl(xmlNode *xml_acls, const char *role, GListPtr *acl);
+static gboolean acl_append(xmlNode *acl_child, GListPtr *acl);
+static void free_acl(GListPtr acl);
+static gboolean parse_acl_xpath(xmlNode *xml, GListPtr acl, GListPtr *parsed_acl);
+
+static gboolean gen_xml_perms(xmlNode *xml, GListPtr acl, GHashTable **xml_perms);
+static int search_xml_children(GListPtr *children, xmlNode *root,
+                  const char *tag, const char *field, const char *value,
+                  gboolean search_matches);
+static int search_xpath_objects(GListPtr *objects, xmlNode *xml_obj, const char *xpath);
+static gboolean update_xml_perms(xmlNode *xml, acl_obj_t *acl_obj, GHashTable *xml_perms);
+static gboolean update_xml_children_perms(xmlNode *xml, const char *mode, GHashTable *xml_perms);
+static void free_xml_perm(gpointer xml_perm);
+
+static gboolean acl_filter_xml(xmlNode *xml, GHashTable *xml_perms);
+static gboolean acl_check_diff_xml(xmlNode *xml, GHashTable *xml_perms);
+
+
+/* rc = TRUE if orig_cib has been filtered*/
+/* That means *filtered_cib rather than orig_cib should be exploited afterwards*/
+gboolean
+acl_filter_cib(xmlNode *request, xmlNode *current_cib, xmlNode *orig_cib, xmlNode **filtered_cib)
+{
+	const char *user = NULL;
+	xmlNode *xml_acls = NULL;
+	xmlNode *tmp_cib = NULL;
+	GListPtr user_acl = NULL;
+	GHashTable *xml_perms = NULL;
+
+	*filtered_cib = NULL;
+
+	if (req_by_superuser(request)) {
+		return FALSE;
+	}
+
+	if (orig_cib == NULL) {
+		return FALSE;
+	}
+
+	if (current_cib == NULL) {
+		return TRUE;
+	}
+
+	user = crm_element_value(request, F_CIB_USER);
+
+	xml_acls = get_object_root(XML_CIB_TAG_ACLS, current_cib);
+	if (xml_acls == NULL) {
+		crm_warn("Ordinary users cannot access the CIB without any defined ACLs: '%s'", user);
+		return TRUE;
+	}
+
+	unpack_user_acl(xml_acls, user, &user_acl);
+
+	tmp_cib = copy_xml(orig_cib);
+
+	gen_xml_perms(tmp_cib, user_acl, &xml_perms);
+
+	if (acl_filter_xml(tmp_cib, xml_perms)) {
+		crm_debug("User '%s' doesn't have the permission for the whole CIB", user);
+		tmp_cib = NULL;
+	}
+
+	g_hash_table_destroy(xml_perms);
+	free_acl(user_acl);
+	
+	*filtered_cib = tmp_cib;
+	return TRUE;
+}
+
+/* rc = TRUE if the request passes the ACL check */
+/* rc = FALSE if the permission is denied */
+gboolean
+acl_check_diff(xmlNode *request, xmlNode *current_cib, xmlNode *result_cib, xmlNode *diff)
+{
+	const char *user = NULL;
+	xmlNode *xml_acls = NULL;
+	GListPtr user_acl = NULL;
+	int rc = FALSE;
+
+	if (req_by_superuser(request)) {
+		return TRUE;
+	}
+
+	if (diff == NULL) {
+		return TRUE;
+	}
+
+	if (current_cib == NULL) {
+		return FALSE;
+	}
+
+	user = crm_element_value(request, F_CIB_USER);
+
+	xml_acls = get_object_root(XML_CIB_TAG_ACLS, current_cib);
+	if (xml_acls == NULL) {
+		crm_warn("Ordinary users cannot access the CIB without any defined ACLs: '%s'", user);
+		return FALSE;
+	}
+
+	unpack_user_acl(xml_acls, user, &user_acl);
+
+	xml_child_iter(
+		diff, diff_child,
+		const char *tag = crm_element_name(diff_child);
+		GListPtr parsed_acl = NULL;
+
+		crm_debug("Preparing ACL checking on '%s'", tag);
+
+		if (crm_str_eq(tag, XML_TAG_DIFF_REMOVED, TRUE)) {
+			crm_debug("Parsing any xpaths under the ACL according to the current CIB");
+			parse_acl_xpath(current_cib, user_acl, &parsed_acl);
+		} else if (crm_str_eq(tag, XML_TAG_DIFF_ADDED, TRUE)) {
+			crm_debug("Parsing any xpaths under the ACL according to the result CIB");
+			parse_acl_xpath(result_cib, user_acl, &parsed_acl);
+		} else {
+			continue;
+		}
+
+		xml_child_iter(
+			diff_child, diff_cib,
+			GHashTable *xml_perms = NULL;
+
+			gen_xml_perms(diff_cib, parsed_acl, &xml_perms);
+			rc = acl_check_diff_xml(diff_cib, xml_perms);
+			g_hash_table_destroy(xml_perms);
+
+			if (rc == FALSE) {
+				crm_warn("User '%s' doesn't have the permission to modify the CIB", user);
+				goto done;
+			}
+			);
+		free_acl(parsed_acl);
+		);
+
+done:
+	free_acl(user_acl);
+	return rc;
+}
+
+static gboolean
+req_by_superuser(xmlNode *request)
+{
+	const char *user = crm_element_value(request, F_CIB_USER);
+	if (user == NULL || crm_str_eq(user, CRM_DAEMON_USER, TRUE)
+			|| crm_str_eq(user, "root", TRUE)) {
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+unpack_user_acl(xmlNode *xml_acls, const char *user, GListPtr *user_acl)
+{
+	if (xml_acls == NULL) {
+		return FALSE;
+	}
+	
+	xml_child_iter(
+        	xml_acls, xml_acl,
+		const char *tag = crm_element_name(xml_acl);
+		const char *id = crm_element_value(xml_acl, XML_ATTR_ID);
+
+		if (crm_str_eq(tag, XML_ACL_TAG_USER, TRUE)) {
+			if (user_match(user, id)) {
+				crm_debug("Unpacking ACL of user: '%s'", id);
+				unpack_acl(xml_acls, xml_acl, user_acl);
+				return TRUE;
+			}
+		}
+		);
+	return FALSE;
+}
+
+static gboolean
+user_match(const char *user, const char *uid)
+{
+	struct passwd *pwent = NULL;
+	int user_uid_num = -1;
+	int uid_num = -1;
+
+	if (crm_str_eq(user, uid, TRUE)) {
+		return TRUE;
+	}
+
+	pwent = getpwnam(user);
+	if (pwent == NULL) {
+		crm_warn("No user named '%s' exists!", user);
+		return FALSE;
+	}
+	user_uid_num = pwent->pw_uid;
+
+	uid_num = crm_int_helper(uid, NULL);
+	if(errno == 0 && uid_num == user_uid_num) {
+		return TRUE;
+        }
+
+	return FALSE;
+}
+
+static gboolean
+unpack_acl(xmlNode *xml_acls, xmlNode *xml_acl, GListPtr *acl)
+{
+	const char *role = crm_element_value(xml_acl, XML_ACL_ATTR_ROLE);
+
+	if (role) {
+		unpack_role_acl(xml_acls, role, acl);
+	}
+
+	xml_child_iter(
+		xml_acl, acl_child,
+		const char *tag = crm_element_name(acl_child);
+
+		if (crm_str_eq(XML_ACL_TAG_READ, tag, TRUE)
+			|| crm_str_eq(XML_ACL_TAG_WRITE, tag, TRUE)
+			|| crm_str_eq(XML_ACL_TAG_DENY, tag, TRUE))
+				acl_append(acl_child, acl);
+		);
+	return TRUE;
+}
+
+static gboolean
+unpack_role_acl(xmlNode *xml_acls, const char *role, GListPtr *acl)
+{
+	xml_child_iter_filter(
+        	xml_acls, xml_acl, XML_ACL_TAG_ROLE,
+		const char *role_id = crm_element_value(xml_acl, XML_ATTR_ID);
+
+		if (role_id && crm_str_eq(role, role_id, TRUE)) {
+			crm_debug("Unpacking ACL of the referenced role: '%s'", role);
+			unpack_acl(xml_acls, xml_acl, acl);
+			return TRUE;
+		}
+		);
+	return FALSE;
+}
+
+static gboolean
+acl_append(xmlNode *acl_child, GListPtr *acl)
+{
+	acl_obj_t *acl_obj = NULL;
+
+	const char *tag = crm_element_value(acl_child, XML_ACL_ATTR_TAG);
+	const char *ref = crm_element_value(acl_child, XML_ACL_ATTR_REF);
+	const char *xpath = crm_element_value(acl_child, XML_ACL_ATTR_XPATH);
+
+	if (tag == NULL && ref == NULL && xpath == NULL) {
+		return FALSE;
+	}
+
+	crm_malloc0(acl_obj, sizeof(acl_obj_t));
+	if (acl_obj == NULL) {
+		return FALSE;
+	}
+
+	acl_obj->mode = crm_element_name(acl_child);
+	acl_obj->tag = tag;
+	acl_obj->ref = ref;
+	acl_obj->xpath = xpath;
+	acl_obj->attribute = crm_element_value(acl_child, XML_ACL_ATTR_ATTRIBUTE);
+
+	*acl = g_list_append(*acl, acl_obj);
+
+	crm_debug_3("ACL object appended: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s",
+			acl_obj->mode, acl_obj->tag, acl_obj->ref, acl_obj->xpath, acl_obj->attribute);
+
+	return TRUE;
+}
+
+static void
+free_acl(GListPtr acl)
+{
+	GListPtr iterator = acl;
+	while(iterator != NULL) {
+		crm_free(iterator->data);
+		iterator = iterator->next;
+	}
+	if(acl != NULL) {
+		g_list_free(acl);
+	}
+}
+
+static gboolean
+parse_acl_xpath(xmlNode *xml, GListPtr acl, GListPtr *parsed_acl)
+{
+	GListPtr acl_iterator = acl;
+	acl_obj_t *new_acl_obj = NULL;
+	
+	*parsed_acl = NULL;
+
+	while (acl_iterator != NULL) {
+		acl_obj_t *acl_obj = acl_iterator->data;
+
+		if (acl_obj->tag || acl_obj->ref) {
+			crm_malloc0(new_acl_obj, sizeof(acl_obj_t));
+			if (new_acl_obj == NULL) {
+				return  FALSE;
+			}
+
+			memcpy(new_acl_obj, acl_obj, sizeof(acl_obj_t));
+
+			*parsed_acl = g_list_append(*parsed_acl, new_acl_obj);
+
+			crm_debug_3("Copied ACL object: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s",
+					new_acl_obj->mode, new_acl_obj->tag, new_acl_obj->ref,
+					new_acl_obj->xpath, new_acl_obj->attribute);
+
+		} else if (acl_obj->xpath) {
+			GListPtr children = NULL;
+			GListPtr children_iterator = NULL;
+
+			search_xpath_objects(&children, xml, acl_obj->xpath);
+
+			children_iterator = children;
+			while (children_iterator != NULL) {
+				crm_malloc0(new_acl_obj, sizeof(acl_obj_t));
+				if (new_acl_obj == NULL) {
+					return  FALSE;
+				}
+
+				new_acl_obj->mode = acl_obj->mode;
+				new_acl_obj->tag = crm_element_name(children_iterator->data);
+				new_acl_obj->ref = crm_element_value(children_iterator->data, XML_ATTR_ID);
+				new_acl_obj->attribute = acl_obj->attribute;
+
+				*parsed_acl = g_list_append(*parsed_acl, new_acl_obj);
+
+				crm_debug_3("Parsed the ACL object with xpath '%s' to: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s",
+						acl_obj->xpath, new_acl_obj->mode, new_acl_obj->tag,
+						new_acl_obj->ref, new_acl_obj->xpath, new_acl_obj->attribute);
+
+				children_iterator = children_iterator->next;
+			}
+			g_list_free(children);
+		}
+		acl_iterator = acl_iterator->next;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+gen_xml_perms(xmlNode *xml, GListPtr acl, GHashTable **xml_perms)
+{
+	GListPtr acl_iterator = acl;
+
+	if (*xml_perms == NULL) {
+		*xml_perms = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_xml_perm);
+	}
+
+	while (acl_iterator != NULL) {
+		acl_obj_t *acl_obj = acl_iterator->data;
+		GListPtr children = NULL;
+		GListPtr children_iterator = NULL;
+
+		crm_debug("Generating permissions with ACL: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s",
+				acl_obj->mode, acl_obj->tag, acl_obj->ref, acl_obj->xpath, acl_obj->attribute);
+		if (acl_obj->tag || acl_obj->ref) {
+			search_xml_children(&children, xml, acl_obj->tag, XML_ATTR_ID, acl_obj->ref, TRUE);
+
+		} else if (acl_obj->xpath) {
+			/* Never be here for a modification operation */
+			/* Already parse_acl_xpath() previously */
+			search_xpath_objects(&children, xml, acl_obj->xpath);
+		}
+
+		children_iterator = children;
+		while (children_iterator != NULL) {
+			update_xml_perms(children_iterator->data, acl_obj, *xml_perms);
+
+			children_iterator = children_iterator->next;
+		}
+		g_list_free(children);
+
+		acl_iterator = acl_iterator->next;
+	}
+	
+	return TRUE;
+}
+
+/* Borrowed from lib/common/xml.c: find_xml_children() */
+/* But adding the original xmlNode pointers into a GList */
+static int
+search_xml_children(GListPtr *children, xmlNode *root,
+		  const char *tag, const char *field, const char *value,
+		  gboolean search_matches)
+{
+	int match_found = 0;
+	
+	CRM_CHECK(root != NULL, return FALSE);
+	CRM_CHECK(children != NULL, return FALSE);
+	
+	if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) {
+
+	} else if(value != NULL
+		  && safe_str_neq(value, crm_element_value(root, field))) {
+
+	} else {
+		*children = g_list_append(*children, root);
+		match_found = 1;
+	}
+
+	if(search_matches || match_found == 0) {
+		xml_child_iter(
+			root, child, 
+			match_found += search_xml_children(
+				children, child, tag, field, value,
+				search_matches);
+			);
+	}
+	
+	return match_found;
+}
+
+static int
+search_xpath_objects(GListPtr *objects, xmlNode *xml_obj, const char *xpath)
+{
+	int match_found = 0;
+	xmlXPathObjectPtr xpathObj = NULL;
+
+	if(xpath == NULL) {
+		return 0;
+	}
+
+	xpathObj = xpath_search(xml_obj, xpath);
+
+	if(xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr < 1) {
+		crm_debug("No match for %s in %s", xpath, xmlGetNodePath(xml_obj));
+
+	} else if(xpathObj->nodesetval->nodeNr > 0) {
+		int lpc = 0, max = xpathObj->nodesetval->nodeNr;
+
+		for(lpc = 0; lpc < max; lpc++) {
+			xmlNode *match = getXpathResult(xpathObj, lpc);
+			if (match == NULL) {
+				continue;
+			}
+
+			*objects = g_list_append(*objects, match);
+			match_found++;
+		}
+	}
+
+	if(xpathObj) {
+		xmlXPathFreeObject(xpathObj);
+	}
+	return match_found;
+}
+
+static gboolean
+update_xml_perms(xmlNode *xml, acl_obj_t *acl_obj, GHashTable *xml_perms)
+{
+	xml_perm_t *perm = NULL;
+
+	if (g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm)) {
+		if (perm->mode != NULL) {
+			return FALSE;
+		}
+	} else {
+		crm_malloc0(perm, sizeof(xml_perm_t));
+		if (perm == NULL) {
+			return FALSE;
+		}
+		g_hash_table_insert(xml_perms, xml, perm);
+	}
+
+	if (acl_obj->attribute == NULL) {
+		perm->mode = acl_obj->mode;
+		crm_debug_3("Permission for element: element_mode=%s, tag=%s, id=%s",
+                                perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID));
+
+		xml_child_iter(
+			xml, child,
+			update_xml_children_perms(child, perm->mode, xml_perms);
+			);
+
+	} else {
+		if (perm->attribute_perms == NULL
+				|| (g_hash_table_lookup_extended(perm->attribute_perms, 
+					acl_obj->attribute, NULL, NULL) == FALSE)) {
+
+			if (perm->attribute_perms == NULL) {
+				perm->attribute_perms = g_hash_table_new_full(
+						g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
+			}
+
+			g_hash_table_insert(perm->attribute_perms,
+					crm_strdup(acl_obj->attribute), crm_strdup(acl_obj->mode));
+			crm_debug_3("Permission for attribute: attribute_mode=%s, tag=%s, id=%s attribute=%s",
+                                	acl_obj->mode, crm_element_name(xml),
+					crm_element_value(xml, XML_ATTR_ID), acl_obj->attribute);
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+update_xml_children_perms(xmlNode *xml, const char *mode, GHashTable *xml_perms)
+{
+	xml_perm_t *perm = NULL;
+
+	if (g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm)) {
+		if (perm->mode != NULL) {
+			return FALSE;
+		}
+	} else {
+		crm_malloc0(perm, sizeof(xml_perm_t));
+		if (perm == NULL) {
+			return FALSE;
+		}
+		g_hash_table_insert(xml_perms, xml, perm);
+	}
+
+	perm->mode = mode;
+	crm_debug_4("Permission for child element: element_mode=%s, tag=%s, id=%s",
+			mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID));
+
+	xml_child_iter(
+		xml, child,
+		update_xml_children_perms(child, mode, xml_perms);
+		);
+
+	return TRUE;
+}
+
+static void
+free_xml_perm(gpointer xml_perm)
+{
+	xml_perm_t *perm = xml_perm;
+
+	if (perm == NULL) {
+		return;
+	}
+
+	if (perm->attribute_perms != NULL) {
+		g_hash_table_destroy(perm->attribute_perms);
+	}
+
+	crm_free(perm);
+}
+
+#define can_read(mode) (crm_str_eq(mode, XML_ACL_TAG_READ, TRUE) \
+			|| crm_str_eq(mode, XML_ACL_TAG_WRITE, TRUE))
+
+#define can_write(mode) crm_str_eq(mode, XML_ACL_TAG_WRITE, TRUE)
+
+/* rc = TRUE if the xml is filtered out*/
+static gboolean
+acl_filter_xml(xmlNode *xml, GHashTable *xml_perms)
+{
+	int children_counter = 0;
+	xml_perm_t *perm = NULL;
+	int allow_counter = 0;
+
+	xml_child_iter(
+		xml, child, 
+		if (acl_filter_xml(child, xml_perms) == FALSE) {
+			children_counter++;
+		}
+		);
+
+	g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm);
+
+	if (perm == NULL) {
+		crm_debug_4("No ACL defined to read the element: tag=%s, id=%s",
+				crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID));
+		goto end_filter;
+	}
+
+	if (perm->attribute_perms == NULL) {
+		if (can_read(perm->mode)) {
+			return FALSE;
+		} else {
+			crm_debug_4("No enough permission to read the element: element_mode=%s, tag=%s, id=%s",
+					 perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID));
+			goto end_filter;
+		}
+	}
+
+	xml_prop_iter(xml, prop_name, prop_value,
+		gpointer mode = NULL;
+
+		if (g_hash_table_lookup_extended(perm->attribute_perms, prop_name, NULL, &mode)) {
+			if (can_read(mode)) {
+				allow_counter++;
+			} else {
+				xml_remove_prop(xml, prop_name);
+				crm_debug_4("Filtered out the attribute: attribute_mode=%s, tag=%s, id=%s, attribute=%s",
+						 (char *)mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name);
+			}
+		} else {
+			if (can_read(perm->mode)) {
+				allow_counter++;
+			} else if (crm_str_eq(prop_name, XML_ATTR_ID, TRUE) == FALSE) {
+				xml_remove_prop(xml, prop_name);
+				crm_debug_4("Filtered out the attribute: element_mode=%s, tag=%s, id=%s, attribute=%s",
+						 perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name);
+			}
+		}
+		);
+
+	if (allow_counter) {
+		return FALSE;
+	}
+
+	if (can_read(perm->mode)) {
+		return FALSE;
+	}
+
+end_filter:
+	if (children_counter) {
+		crm_debug_4("Don't filter out the element (tag=%s, id=%s) because user can read its children",
+				crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID));
+		return FALSE;
+	}
+
+	free_xml_from_parent(NULL, xml);
+	crm_debug_4("Filtered out the element: tag=%s, id=%s",
+			crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID));
+	return TRUE;
+}
+
+static gboolean
+acl_check_diff_xml(xmlNode *xml, GHashTable *xml_perms)
+{
+	xml_perm_t *perm = NULL;
+
+	xml_child_iter(
+		xml, child, 
+		if (acl_check_diff_xml(child, xml_perms) == FALSE) {
+			return FALSE;
+		}
+		);
+
+	g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm);
+
+	xml_prop_iter(xml, prop_name, prop_value,
+		gpointer mode = NULL;
+
+		if (crm_str_eq(crm_element_name(xml), XML_TAG_CIB, TRUE)) {
+			if (crm_str_eq(prop_name, XML_ATTR_GENERATION, TRUE)
+					|| crm_str_eq(prop_name, XML_ATTR_NUMUPDATES, TRUE)
+					|| crm_str_eq(prop_name, XML_ATTR_GENERATION_ADMIN, TRUE)) {
+				continue;
+			}
+		}
+
+		if (crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) {
+			continue;
+		}
+
+		if (perm == NULL) {
+			crm_debug("No ACL defined to modify the element: tag=%s, id=%s, attribute=%s",
+					 crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name);
+			return FALSE;
+		}
+
+		if (perm->attribute_perms == NULL) {
+			if (can_write(perm->mode)) {
+				return TRUE;
+			} else {
+				crm_debug("No enough permission to modify the element: element_mode=%s, tag=%s, id=%s, attribute=%s",
+						 perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name);
+				return FALSE;
+			}
+		}
+
+		if (g_hash_table_lookup_extended(perm->attribute_perms, prop_name, NULL, &mode)) {
+			if (can_write(mode) == FALSE) {
+				crm_debug("No enough permission to modify the attribute: attribute_mode=%s, tag=%s, id=%s, attribute=%s",
+						 (char *)mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name);
+				return FALSE;
+			}
+		} else if (can_write(perm->mode) == FALSE) {
+			crm_debug("No enough permission to modify the element and the attribute: element_mode=%s, tag=%s, id=%s, attribute=%s",
+					 perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name);
+			return FALSE;
+		}
+
+		);
+
+	return TRUE;
+}
+
diff -r ae2f826609ed -r 7fecd951b21c lib/cib/cib_native.c
--- a/lib/cib/cib_native.c	Mon Jan 25 10:54:01 2010 +0100
+++ b/lib/cib/cib_native.c	Mon Feb 01 22:54:38 2010 +0800
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
+#include <pwd.h>
 
 #include <glib.h>
 
@@ -155,9 +156,19 @@
 	}
 	
 	if(rc == cib_ok) {
+	    int uid = geteuid();
+	    struct passwd *pwent = NULL;
+
 	    CRM_CHECK(native->token != NULL, ;);
 	    hello = cib_create_op(0, native->token, CRM_OP_REGISTER, NULL, NULL, NULL, 0);
 	    crm_xml_add(hello, F_CIB_CLIENTNAME, name);
+
+	    pwent = getpwuid(uid);
+	    if (pwent == NULL) {
+		crm_perror(LOG_ERR, "Cannot get name for uid: %d", uid);
+	    } else {
+		crm_xml_add(hello, F_CIB_USER, crm_strdup(pwent->pw_name));
+	    }
 	    
 	    if(send_ipc_message(native->command_channel, hello) == FALSE) {
 		rc = cib_callback_register;
diff -r ae2f826609ed -r 7fecd951b21c lib/cib/cib_private.h
--- a/lib/cib/cib_private.h	Mon Jan 25 10:54:01 2010 +0100
+++ b/lib/cib/cib_private.h	Mon Feb 01 22:54:38 2010 +0800
@@ -71,5 +71,8 @@
     cib_t *cib, int call_id, int timeout, gboolean only_success, void *user_data,
     const char *callback_name, void (*callback)(xmlNode*, int, int, xmlNode*,void*));
 
+extern gboolean acl_filter_cib(xmlNode *request, xmlNode *current_cib, xmlNode *orig_cib, xmlNode **filtered_cib);
+extern gboolean acl_check_diff(xmlNode *request, xmlNode *current_cib, xmlNode *result_cib, xmlNode *diff);
+
 
 #endif
diff -r ae2f826609ed -r 7fecd951b21c lib/cib/cib_utils.c
--- a/lib/cib/cib_utils.c	Mon Jan 25 10:54:01 2010 +0100
+++ b/lib/cib/cib_utils.c	Mon Feb 01 22:54:38 2010 +0800
@@ -56,6 +56,7 @@
     { XML_CIB_TAG_CONSTRAINTS,  "/cib/configuration", "//cib/configuration/constraints" },
     { XML_CIB_TAG_OPCONFIG,	"/cib/configuration", "//cib/configuration/op_defaults" },
     { XML_CIB_TAG_RSCCONFIG,	"/cib/configuration", "//cib/configuration/rsc_defaults" },
+    { XML_CIB_TAG_ACLS,		"/cib/configuration", "//cib/configuration/acls" },
     { XML_CIB_TAG_SECTION_ALL,  NULL,                 "//cib" },
 };
 
@@ -225,6 +226,9 @@
 			break;
 		case cib_transform_failed:
 			error_msg = "Schema transform failed";
+			break;
+		case cib_permission_denied:
+			error_msg = "Permission Denied";
 			break;
 	}
 			
diff -r ae2f826609ed -r 7fecd951b21c xml/pacemaker.rng.in
--- a/xml/pacemaker.rng.in	Mon Jan 25 10:54:01 2010 +0100
+++ b/xml/pacemaker.rng.in	Mon Feb 01 22:54:38 2010 +0800
@@ -44,6 +44,9 @@
 	<element name="constraints">
 	  <externalRef href="constrain...@crm_dtd_version@.rng"/>
 	</element>
+	<optional>
+	  <ref name="element-acls"/>
+	</optional>
       </interleave>
     </element>
     <element name="status">
@@ -134,4 +137,58 @@
     </zeroOrMore>
   </define>
 
+  <define name="element-acls">
+    <element name="acls">
+      <zeroOrMore>
+	<choice>
+	  <element name="user">
+	    <attribute name="id"><text/></attribute>
+	    <choice>
+	      <attribute name="role"><data type="IDREF"/></attribute>
+	      <zeroOrMore>
+		<ref name="element-acl"/>
+	      </zeroOrMore>
+	    </choice>
+	  </element>
+	  <element name="role">
+	    <attribute name="id"><data type="ID"/></attribute>
+	    <zeroOrMore>
+	      <ref name="element-acl"/>
+	    </zeroOrMore>
+	  </element>
+	</choice>
+      </zeroOrMore>
+    </element>
+  </define>
+
+  <define name="element-acl">
+    <choice>
+      <element name="read">
+	<ref name="attribute-acl"/>
+      </element>
+      <element name="write">
+	<ref name="attribute-acl"/>
+      </element>
+      <element name="deny">
+	<ref name="attribute-acl"/>
+      </element>
+    </choice>
+  </define>
+
+  <define name="attribute-acl">
+    <attribute name="id"><data type="ID"/></attribute>
+      <choice>
+	<attribute name="tag"><text/></attribute>
+	<attribute name="ref"><data type="IDREF"/></attribute>
+	<group>
+	  <attribute name="tag"><text/></attribute>
+	  <attribute name="ref"><data type="IDREF"/></attribute>
+	</group>
+	<attribute name="xpath"><text/></attribute>
+      </choice>
+      <optional>
+	<attribute name="attribute"><text/></attribute>
+      </optional>
+  </define>
+
 </grammar>
_______________________________________________
Pacemaker mailing list
Pacemaker@oss.clusterlabs.org
http://oss.clusterlabs.org/mailman/listinfo/pacemaker

Reply via email to