[libgdata] core: Add support for ACL entries with keys



commit f817fdaff889764125c5f073be4fd433c5c33b14
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Sun Aug 24 16:05:42 2014 +0100

    core: Add support for ACL entries with keys
    
    This adds support for <gAcl:withKey/> elements, allowing for
    authorisation keys in ACLs, e.g. for Google Documents.
    
    This adds the following API:
     • GDataAccessRule:key
     • gdata_access_rule_get_key()
    and appropriate tests.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=690628

 docs/reference/gdata-sections.txt |    1 +
 gdata/gdata-access-rule.c         |   97 ++++++++++++++++++++++++++++++++++++-
 gdata/gdata-access-rule.h         |    1 +
 gdata/gdata.symbols               |    1 +
 gdata/tests/general.c             |   59 ++++++++++++++++++++++-
 5 files changed, 156 insertions(+), 3 deletions(-)
---
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 5426402..fdcac2f 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -691,6 +691,7 @@ gdata_access_rule_set_role
 gdata_access_rule_get_scope
 gdata_access_rule_set_scope
 gdata_access_rule_get_edited
+gdata_access_rule_get_key
 <SUBSECTION Standard>
 gdata_access_rule_get_type
 GDATA_ACCESS_RULE
diff --git a/gdata/gdata-access-rule.c b/gdata/gdata-access-rule.c
index 51409ac..220c2a4 100644
--- a/gdata/gdata-access-rule.c
+++ b/gdata/gdata-access-rule.c
@@ -2,7 +2,7 @@
 /*
  * GData Client
  * Copyright (C) Thibault Saunier 2009 <saunierthibault gmail com>
- * Copyright (C) Philip Withnall 2009–2010 <philip tecnocode co uk>
+ * Copyright (C) Philip Withnall 2009–2010, 2014 <philip tecnocode co uk>
  *
  * GData Client is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -99,6 +99,7 @@ struct _GDataAccessRulePrivate {
        gchar *scope_type;
        gchar *scope_value;
        gint64 edited;
+       gchar *key;
 };
 
 enum {
@@ -106,7 +107,8 @@ enum {
        PROP_SCOPE_TYPE,
        PROP_SCOPE_VALUE,
        PROP_EDITED,
-       PROP_ETAG
+       PROP_ETAG,
+       PROP_KEY,
 };
 
 G_DEFINE_TYPE (GDataAccessRule, gdata_access_rule, GDATA_TYPE_ENTRY)
@@ -190,6 +192,22 @@ gdata_access_rule_class_init (GDataAccessRuleClass *klass)
                                                             -1, G_MAXINT64, -1,
                                                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
+       /**
+        * GDataAccessRule:key:
+        *
+        * An optional authorisation key required to access this item with the given scope. If set, this 
restricts
+        * access to those principals who have a copy of the key. The key is generated server-side and cannot 
be
+        * modified by the client. If no authorisation key is set (and hence none is needed for access to the 
item),
+        * this will be %NULL.
+        *
+        * Since: UNRELEASED
+        */
+       g_object_class_install_property (gobject_class, PROP_KEY,
+                                        g_param_spec_string ("key",
+                                                             "Key", "An optional authorisation key required 
to access this item.",
+                                                             NULL,
+                                                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
        /* Override the ETag property since ETags don't seem to be supported for ACL entries. TODO: 
Investigate this further (might only be
         * unsupported for Google Calendar). */
        g_object_class_override_property (gobject_class, PROP_ETAG, "etag");
@@ -260,6 +278,7 @@ gdata_access_rule_finalize (GObject *object)
        g_free (priv->role);
        g_free (priv->scope_type);
        g_free (priv->scope_value);
+       g_free (priv->key);
 
        /* Chain up to the parent class */
        G_OBJECT_CLASS (gdata_access_rule_parent_class)->finalize (object);
@@ -287,6 +306,9 @@ gdata_access_rule_get_property (GObject *object, guint property_id, GValue *valu
                        /* Never return an ETag */
                        g_value_set_string (value, NULL);
                        break;
+               case PROP_KEY:
+                       g_value_set_string (value, priv->key);
+                       break;
                default:
                        /* We don't have any other property... */
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -316,6 +338,11 @@ gdata_access_rule_set_property (GObject *object, guint property_id, const GValue
                case PROP_ETAG:
                        /* Never set an ETag (note that this doesn't stop it being set in GDataEntry due to 
XML parsing) */
                        break;
+               case PROP_KEY:
+                       g_free (self->priv->key);
+                       self->priv->key = g_value_dup_string (value);
+                       g_object_notify (object, "key");
+                       break;
                default:
                        /* We don't have any other property... */
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -358,6 +385,46 @@ parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_da
 
                        self->priv->scope_type = (gchar*) scope_type;
                        self->priv->scope_value = (gchar*) scope_value;
+               } else if (xmlStrcmp (node->name, (xmlChar*) "withKey") == 0) {
+                       /* gAcl:withKey */
+                       gboolean found_role = FALSE;
+                       xmlNode *child;
+                       xmlChar *key;
+
+                       /* Extract the key. */
+                       key = xmlGetProp (node, (xmlChar *) "key");
+                       if (key == NULL) {
+                               return gdata_parser_error_required_property_missing (node, "key", error);
+                       }
+
+                       self->priv->key = (gchar *) key;
+
+                       /* Look for a gAcl:role child element. */
+                       for (child = node->children; child != NULL; child = child->next) {
+                               if (xmlStrcmp (child->name, (xmlChar*) "role") == 0) {
+                                       xmlChar *role = xmlGetProp (child, (xmlChar *) "value");
+                                       if (role == NULL) {
+                                               return gdata_parser_error_required_property_missing (child, 
"value", error);
+                                       }
+
+                                       self->priv->role = (gchar *) role;
+                                       found_role = TRUE;
+                               } else {
+                                       /* TODO: this logic copied from gdata-parsable.c.  Re-evaluate this 
at some point in the future.
+                                        * If GeoRSS and GML support were to be used more widely, it might 
due to implement GML objects. */
+                                       xmlBuffer *buffer;
+
+                                       /* Unhandled XML */
+                                       buffer = xmlBufferCreate ();
+                                       xmlNodeDump (buffer, doc, child, 0, 0);
+                                       g_debug ("Unhandled XML in <gAcl:withKey>: %s", (gchar *) 
xmlBufferContent (buffer));
+                                       xmlBufferFree (buffer);
+                               }
+                       }
+
+                       if (!found_role) {
+                               return gdata_parser_error_required_element_missing ("role", "gAcl:withKey", 
error);
+                       }
                } else {
                        return GDATA_PARSABLE_CLASS (gdata_access_rule_parent_class)->parse_xml (parsable, 
doc, node, user_data, error);
                }
@@ -376,11 +443,20 @@ get_xml (GDataParsable *parsable, GString *xml_string)
        /* Chain up to the parent class */
        GDATA_PARSABLE_CLASS (gdata_access_rule_parent_class)->get_xml (parsable, xml_string);
 
+       if (priv->key != NULL) {
+               /* gAcl:withKey; has to wrap gAcl:role */
+               gdata_parser_string_append_escaped (xml_string, "<gAcl:withKey key='", priv->key, "'>");
+       }
+
        if (priv->role != NULL) {
                /* gAcl:role */
                gdata_parser_string_append_escaped (xml_string, "<gAcl:role value='", priv->role, "'/>");
        }
 
+       if (priv->key != NULL) {
+               g_string_append (xml_string, "</gAcl:withKey>");
+       }
+
        if (priv->scope_value != NULL) {
                /* gAcl:scope */
                if (priv->scope_type != NULL) {
@@ -531,3 +607,20 @@ gdata_access_rule_get_edited (GDataAccessRule *self)
        g_return_val_if_fail (GDATA_IS_ACCESS_RULE (self), -1);
        return self->priv->edited;
 }
+
+/**
+ * gdata_access_rule_get_key:
+ * @self: a #GDataAccessRule
+ *
+ * Gets the #GDataAccessRule:key property.
+ *
+ * Return value: the access rule's authorisation key, or %NULL
+ *
+ * Since: UNRELEASED
+ */
+const gchar *
+gdata_access_rule_get_key (GDataAccessRule *self)
+{
+       g_return_val_if_fail (GDATA_IS_ACCESS_RULE (self), NULL);
+       return self->priv->key;
+}
diff --git a/gdata/gdata-access-rule.h b/gdata/gdata-access-rule.h
index 483a871..29409a9 100644
--- a/gdata/gdata-access-rule.h
+++ b/gdata/gdata-access-rule.h
@@ -106,6 +106,7 @@ void gdata_access_rule_set_role (GDataAccessRule *self, const gchar *role);
 void gdata_access_rule_get_scope (GDataAccessRule *self, const gchar **type, const gchar **value);
 void gdata_access_rule_set_scope (GDataAccessRule *self, const gchar *type, const gchar *value);
 gint64 gdata_access_rule_get_edited (GDataAccessRule *self);
+const gchar *gdata_access_rule_get_key (GDataAccessRule *self) G_GNUC_PURE;
 
 G_END_DECLS
 
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index 3f3c1f0..5b83a42 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -307,6 +307,7 @@ gdata_access_rule_set_role
 gdata_access_rule_get_scope
 gdata_access_rule_set_scope
 gdata_access_rule_get_edited
+gdata_access_rule_get_key
 gdata_parsable_get_type
 gdata_parsable_new_from_xml
 gdata_parsable_get_xml
diff --git a/gdata/tests/general.c b/gdata/tests/general.c
index f920727..237a908 100644
--- a/gdata/tests/general.c
+++ b/gdata/tests/general.c
@@ -1471,7 +1471,7 @@ static void
 test_access_rule_get_xml (void)
 {
        GDataAccessRule *rule, *rule2;
-       gchar *xml, *role, *scope_type3, *scope_value3;
+       gchar *xml, *role, *scope_type3, *scope_value3, *key;
        gint64 edited, edited2;
        const gchar *scope_type, *scope_value, *scope_type2, *scope_value2;
        GError *error = NULL;
@@ -1491,6 +1491,7 @@ test_access_rule_get_xml (void)
        g_assert_cmpstr (scope_value, ==, "A scope value");
        edited = gdata_access_rule_get_edited (rule);
        g_assert_cmpuint (edited, >, 0); /* current time */
+       g_assert_cmpstr (gdata_access_rule_get_key (rule), ==, NULL);
 
        /* Set the properties more conventionally */
        gdata_access_rule_set_role (rule, GDATA_ACCESS_ROLE_NONE);
@@ -1524,6 +1525,7 @@ test_access_rule_get_xml (void)
        g_assert_cmpstr (scope_value, ==, scope_value2);
        edited = gdata_access_rule_get_edited (rule2);
        g_assert_cmpuint (edited, ==, -1); /* unspecified in XML */
+       g_assert_cmpstr (gdata_access_rule_get_key (rule), ==, gdata_access_rule_get_key (rule2));
 
        /* Check properties a different way */
        g_object_get (G_OBJECT (rule2),
@@ -1531,12 +1533,14 @@ test_access_rule_get_xml (void)
                      "scope-type", &scope_type3,
                      "scope-value", &scope_value3,
                      "edited", &edited2,
+                     "key", &key,
                      NULL);
 
        g_assert_cmpstr (role, ==, gdata_access_rule_get_role (rule));
        g_assert_cmpstr (scope_type, ==, scope_type3);
        g_assert_cmpstr (scope_value, ==, scope_value3);
        g_assert_cmpuint (edited2, ==, -1);
+       g_assert_cmpstr (key, ==, NULL);
 
        g_free (role);
        g_free (scope_type3);
@@ -1591,6 +1595,53 @@ test_access_rule_get_xml (void)
 }
 
 static void
+test_access_rule_get_xml_with_key (void)
+{
+       GDataAccessRule *rule;
+       gint64 edited;
+       const gchar *scope_type, *scope_value;
+       GError *error = NULL;
+
+       rule = GDATA_ACCESS_RULE (gdata_parsable_new_from_xml (GDATA_TYPE_ACCESS_RULE,
+               "<?xml version='1.0' encoding='UTF-8'?>"
+               "<entry xmlns='http://www.w3.org/2005/Atom' "
+                      "xmlns:gd='http://schemas.google.com/g/2005' "
+                      "xmlns:gAcl='http://schemas.google.com/acl/2007'>"
+                       "<title type='text'>none</title>"
+                       "<id>an-id</id>"
+                       "<category term='http://schemas.google.com/acl/2007#accessRule' 
scheme='http://schemas.google.com/g/2005#kind'/>"
+                       "<gAcl:withKey key='asdasd'><gAcl:role value='none'/></gAcl:withKey>"
+                       "<gAcl:scope type='user' value='foo example com'/>"
+               "</entry>", -1, &error));
+       g_assert_no_error (error);
+       g_assert (GDATA_IS_ACCESS_RULE (rule));
+
+       /* Check the properties. */
+       g_assert_cmpstr (gdata_access_rule_get_role (rule), ==, "none");
+       gdata_access_rule_get_scope (rule, &scope_type, &scope_value);
+       g_assert_cmpstr (scope_type, ==, "user");
+       g_assert_cmpstr (scope_value, ==, "foo example com");
+       edited = gdata_access_rule_get_edited (rule);
+       g_assert_cmpuint (edited, >, 0); /* current time */
+       g_assert_cmpstr (gdata_access_rule_get_key (rule), ==, "asdasd");
+
+       /* Check the outputted XML is the same. */
+       gdata_test_assert_xml (rule,
+               "<?xml version='1.0' encoding='UTF-8'?>"
+               "<entry xmlns='http://www.w3.org/2005/Atom' "
+                      "xmlns:gd='http://schemas.google.com/g/2005' "
+                      "xmlns:gAcl='http://schemas.google.com/acl/2007'>"
+                       "<title type='text'>none</title>"
+                       "<id>an-id</id>"
+                       "<category term='http://schemas.google.com/acl/2007#accessRule' 
scheme='http://schemas.google.com/g/2005#kind'/>"
+                       "<gAcl:withKey key='asdasd'><gAcl:role value='none'/></gAcl:withKey>"
+                       "<gAcl:scope type='user' value='foo example com'/>"
+               "</entry>");
+
+       g_object_unref (rule);
+}
+
+static void
 test_access_rule_error_handling (void)
 {
        GDataAccessRule *rule;
@@ -1616,6 +1667,11 @@ test_access_rule_error_handling (void)
        TEST_XML_ERROR_HANDLING ("<app:edited/>"); /* missing date */
        TEST_XML_ERROR_HANDLING ("<app:edited>not a date</app:edited>"); /* bad date */
 
+       /* withKey */
+       TEST_XML_ERROR_HANDLING ("<gAcl:withKey><gAcl:role value='none'/></gAcl:withKey>");  /* missing key */
+       TEST_XML_ERROR_HANDLING ("<gAcl:withKey key='asd'/>");  /* missing role */
+       TEST_XML_ERROR_HANDLING ("<gAcl:withKey key='asd'><gAcl:role/></gAcl:withKey>");  /* missing role */
+
 #undef TEST_XML_ERROR_HANDLING
 }
 
@@ -4527,6 +4583,7 @@ main (int argc, char *argv[])
        g_test_add_func ("/query/etag", test_query_etag);
 
        g_test_add_func ("/access-rule/get_xml", test_access_rule_get_xml);
+       g_test_add_func ("/access-rule/get_xml/with_key", test_access_rule_get_xml_with_key);
        g_test_add_func ("/access-rule/error_handling", test_access_rule_error_handling);
        g_test_add_func ("/access-rule/escaping", test_access_rule_escaping);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]