Blame SOURCES/satyr-0.13-ureport-auth-support.patch

01add4
From 3e11b3c67bc31662810f72571311d7d7580e3f0a Mon Sep 17 00:00:00 2001
01add4
From: Jakub Filak <jfilak@redhat.com>
01add4
Date: Mon, 18 Aug 2014 20:39:34 +0200
01add4
Subject: [SATYR PATCH 6/6] Add authentication data support in uReport
01add4
01add4
Support authentication data in uReport
01add4
01add4
We need this for 'machine id' element which should be present only if
01add4
users wishes to it.
01add4
01add4
The authentication entries are string based, key value pairs (JSON
01add4
string elements).
01add4
01add4
The entries are enclosed in an envelope object called 'auth'.
01add4
01add4
Example:
01add4
01add4
"auth" : {   "machineid" : "deadbeaf"
01add4
         }
01add4
01add4
Fixes #171
01add4
01add4
Signed-off-by: Jakub Filak <jfilak@redhat.com>
01add4
Signed-off-by: Martin Milata <mmilata@redhat.com>
01add4
01add4
mmilata: s/custom/auth/g, python bindings
01add4
01add4
tests: add a test for uReport auth data
01add4
01add4
Related to #171
01add4
01add4
Signed-off-by: Jakub Filak <jfilak@redhat.com>
01add4
Signed-off-by: Martin Milata <mmilata@redhat.com>
01add4
01add4
mmilata: python test
01add4
01add4
ureport: fix indentation of 'auth' key in JSON
01add4
01add4
Fixes a bug in commit 189f7adbba9c38bd434c30e7d0c07b546c373312
01add4
01add4
Related to #171
01add4
01add4
Signed-off-by: Jakub Filak <jfilak@redhat.com>
01add4
---
01add4
 include/report.h                |  17 ++++++
01add4
 lib/internal_utils.h            |  12 +++++
01add4
 lib/json.c                      |  26 ++++++++++
01add4
 lib/report.c                    |  82 +++++++++++++++++++++++++++++
01add4
 python/py_report.c              |  30 +++++++++++
01add4
 python/py_report.h              |   2 +
01add4
 tests/json_files/ureport-1-auth | 112 ++++++++++++++++++++++++++++++++++++++++
01add4
 tests/python/report.py          |   6 +++
01add4
 tests/report.at                 |  81 +++++++++++++++++++++++++++++
01add4
 9 files changed, 368 insertions(+)
01add4
 create mode 100644 tests/json_files/ureport-1-auth
01add4
01add4
diff --git a/include/report.h b/include/report.h
01add4
index 424b730..025d8f8 100644
01add4
--- a/include/report.h
01add4
+++ b/include/report.h
01add4
@@ -31,6 +31,13 @@ extern "C" {
01add4
 struct sr_json_value;
01add4
 struct sr_stacktrace;
01add4
 
01add4
+struct sr_report_custom_entry
01add4
+{
01add4
+    char *key;
01add4
+    char *value;
01add4
+    struct sr_report_custom_entry *next;
01add4
+};
01add4
+
01add4
 struct sr_report
01add4
 {
01add4
     uint32_t report_version;
01add4
@@ -49,6 +56,8 @@ struct sr_report
01add4
     struct sr_rpm_package *rpm_packages;
01add4
 
01add4
     struct sr_stacktrace *stacktrace;
01add4
+
01add4
+    struct sr_report_custom_entry *auth_entries;
01add4
 };
01add4
 
01add4
 struct sr_report *
01add4
@@ -60,6 +69,14 @@ sr_report_init(struct sr_report *report);
01add4
 void
01add4
 sr_report_free(struct sr_report *report);
01add4
 
01add4
+
01add4
+/* @brief Adds a new entry to 'auth' object
01add4
+ *
01add4
+ * The implementation is LIFO. The resulting list is in reversed.
01add4
+ */
01add4
+void
01add4
+sr_report_add_auth(struct sr_report *report, const char *key, const char *value);
01add4
+
01add4
 char *
01add4
 sr_report_to_json(struct sr_report *report);
01add4
 
01add4
diff --git a/lib/internal_utils.h b/lib/internal_utils.h
01add4
index 20f65de..3a10520 100644
01add4
--- a/lib/internal_utils.h
01add4
+++ b/lib/internal_utils.h
01add4
@@ -130,4 +130,16 @@ json_read_bool(struct sr_json_value *object, const char *key_name, bool *dest,
01add4
         abort();                                                                      \
01add4
     }
01add4
 
01add4
+/* returns the number of object's children */
01add4
+unsigned
01add4
+json_object_children_count(struct sr_json_value *object);
01add4
+
01add4
+/* returns the child_noth child and passes its name in child_name arg */
01add4
+struct sr_json_value *
01add4
+json_object_get_child(struct sr_json_value *object, unsigned child_no, const char **child_name);
01add4
+
01add4
+/* returns string's value */
01add4
+const char *
01add4
+json_string_get_value(struct sr_json_value *object);
01add4
+
01add4
 #endif
01add4
diff --git a/lib/json.c b/lib/json.c
01add4
index 35b7e6b..d777973 100644
01add4
--- a/lib/json.c
01add4
+++ b/lib/json.c
01add4
@@ -787,6 +787,32 @@ json_element(struct sr_json_value *object, const char *key_name)
01add4
     return NULL;
01add4
 }
01add4
 
01add4
+unsigned
01add4
+json_object_children_count(struct sr_json_value *object)
01add4
+{
01add4
+    assert(object->type == SR_JSON_OBJECT);
01add4
+
01add4
+    return object->u.object.length;
01add4
+}
01add4
+
01add4
+struct sr_json_value *
01add4
+json_object_get_child(struct sr_json_value *object, unsigned child_no, const char **child_name)
01add4
+{
01add4
+    assert(object->type == SR_JSON_OBJECT);
01add4
+    assert(child_no < object->u.object.length);
01add4
+
01add4
+    *child_name = object->u.object.values[child_no].name;
01add4
+    return object->u.object.values[child_no].value;
01add4
+}
01add4
+
01add4
+const char *
01add4
+json_string_get_value(struct sr_json_value *object)
01add4
+{
01add4
+    assert(object->type == SR_JSON_STRING);
01add4
+
01add4
+    return object->u.string.ptr;
01add4
+}
01add4
+
01add4
 #define DEFINE_JSON_READ(name, c_type, json_type, json_member, conversion)                       \
01add4
     bool                                                                                         \
01add4
     name(struct sr_json_value *object, const char *key_name, c_type *dest, char **error_message) \
01add4
diff --git a/lib/report.c b/lib/report.c
01add4
index a6ecae4..411df64 100644
01add4
--- a/lib/report.c
01add4
+++ b/lib/report.c
01add4
@@ -66,6 +66,7 @@ sr_report_init(struct sr_report *report)
01add4
     report->component_name = NULL;
01add4
     report->rpm_packages = NULL;
01add4
     report->stacktrace = NULL;
01add4
+    report->auth_entries = NULL;
01add4
 }
01add4
 
01add4
 void
01add4
@@ -75,9 +76,36 @@ sr_report_free(struct sr_report *report)
01add4
     sr_operating_system_free(report->operating_system);
01add4
     sr_rpm_package_free(report->rpm_packages, true);
01add4
     sr_stacktrace_free(report->stacktrace);
01add4
+
01add4
+    struct sr_report_custom_entry *iter = report->auth_entries;
01add4
+    while (iter)
01add4
+    {
01add4
+        struct sr_report_custom_entry *tmp = iter->next;
01add4
+
01add4
+        free(iter->value);
01add4
+        free(iter->key);
01add4
+        free(iter);
01add4
+
01add4
+        iter = tmp;
01add4
+    }
01add4
+
01add4
     free(report);
01add4
 }
01add4
 
01add4
+void
01add4
+sr_report_add_auth(struct sr_report *report, const char *key, const char *value)
01add4
+{
01add4
+    struct sr_report_custom_entry *new_entry = sr_malloc(sizeof(*new_entry));
01add4
+    new_entry->key = sr_strdup(key);
01add4
+    new_entry->value = sr_strdup(value);
01add4
+
01add4
+    /* prepend the new value
01add4
+     * it is much faster and easier
01add4
+     * we can do it because we do not to preserve the order(?) */
01add4
+    new_entry->next = report->auth_entries;
01add4
+    report->auth_entries = new_entry;
01add4
+}
01add4
+
01add4
 /* The object has to be non-empty, i.e. contain at least one key-value pair. */
01add4
 static void
01add4
 dismantle_object(char *obj)
01add4
@@ -221,6 +249,34 @@ sr_report_to_json(struct sr_report *report)
01add4
         free(rpms_str_indented);
01add4
     }
01add4
 
01add4
+    /* Custom entries.
01add4
+     *    "auth" : {   "foo": "blah"
01add4
+     *             ,   "one": "two"
01add4
+     *             }
01add4
+     */
01add4
+    struct sr_report_custom_entry *iter = report->auth_entries;
01add4
+    if (iter)
01add4
+    {
01add4
+        sr_strbuf_append_strf(strbuf, ",   \"auth\": {   ");
01add4
+        sr_json_append_escaped(strbuf, iter->key);
01add4
+        sr_strbuf_append_str(strbuf, ": ");
01add4
+        sr_json_append_escaped(strbuf, iter->value);
01add4
+        sr_strbuf_append_str(strbuf, "\n");
01add4
+
01add4
+        /* the first entry is prefix with '{', see lines above */
01add4
+        iter = iter->next;
01add4
+        while (iter)
01add4
+        {
01add4
+            sr_strbuf_append_strf(strbuf, "            ,   ");
01add4
+            sr_json_append_escaped(strbuf, iter->key);
01add4
+            sr_strbuf_append_str(strbuf, ": ");
01add4
+            sr_json_append_escaped(strbuf, iter->value);
01add4
+            sr_strbuf_append_str(strbuf, "\n");
01add4
+            iter = iter->next;
01add4
+        }
01add4
+        sr_strbuf_append_str(strbuf, "            } ");
01add4
+    }
01add4
+
01add4
     sr_strbuf_append_str(strbuf, "}");
01add4
     return sr_strbuf_free_nobuf(strbuf);
01add4
 }
01add4
@@ -336,6 +392,32 @@ sr_report_from_json(struct sr_json_value *root, char **error_message)
01add4
 
01add4
     }
01add4
 
01add4
+    /* Authentication entries. */
01add4
+    struct sr_json_value *extra = json_element(root, "auth");
01add4
+    if (extra)
01add4
+    {
01add4
+        if (!JSON_CHECK_TYPE(extra, SR_JSON_OBJECT, "auth"))
01add4
+            goto fail;
01add4
+
01add4
+        const unsigned children = json_object_children_count(extra);
01add4
+
01add4
+        /* from the last children down to the first for easier testing :)
01add4
+         * keep it as it is as long as sr_report_add_auth() does LIFO */
01add4
+        for (unsigned i = 1; i <= children; ++i)
01add4
+        {
01add4
+            const char *child_name = NULL;
01add4
+            struct sr_json_value *child_object = json_object_get_child(extra,
01add4
+                                                                       children - i,
01add4
+                                                                       &child_name);
01add4
+
01add4
+            if (!JSON_CHECK_TYPE(child_object, SR_JSON_STRING, child_name))
01add4
+                continue;
01add4
+
01add4
+            const char *child_value = json_string_get_value(child_object);
01add4
+            sr_report_add_auth(report, child_name, child_value);
01add4
+        }
01add4
+    }
01add4
+
01add4
     return report;
01add4
 
01add4
 fail:
01add4
diff --git a/python/py_report.c b/python/py_report.c
01add4
index d314027..8aa35dd 100644
01add4
--- a/python/py_report.c
01add4
+++ b/python/py_report.c
01add4
@@ -42,6 +42,8 @@
01add4
 #define to_json_doc "Usage: report.to_json()\n\n" \
01add4
                     "Returns: string - the report serialized as JSON"
01add4
 
01add4
+#define auth_doc "Dictinary of key/value pairs used for authentication"
01add4
+
01add4
 /* See python/py_common.h and python/py_gdb_frame.c for generic getters/setters documentation. */
01add4
 #define GSOFF_PY_STRUCT sr_py_report
01add4
 #define GSOFF_PY_MEMBER report
01add4
@@ -64,6 +66,7 @@ report_getset[] =
01add4
     SR_ATTRIBUTE_STRING(component_name,   "Name of the software component this report pertains to (string)"       ),
01add4
     { (char*)"report_version", sr_py_report_get_version, sr_py_setter_readonly, (char*)"Version of the report (int)", NULL },
01add4
     { (char*)"report_type", sr_py_report_get_type, sr_py_report_set_type, (char*)"Report type (string)", NULL },
01add4
+    { (char*)"auth",        sr_py_report_get_auth, sr_py_report_set_auth, (char*)auth_doc, NULL               },
01add4
     { NULL },
01add4
 };
01add4
 
01add4
@@ -492,3 +495,30 @@ sr_py_report_to_json(PyObject *self, PyObject *args)
01add4
     free(json);
01add4
     return result;
01add4
 }
01add4
+
01add4
+PyObject *
01add4
+sr_py_report_get_auth(PyObject *self, void *data)
01add4
+{
01add4
+    struct sr_report *report = ((struct sr_py_report *)self)->report;
01add4
+    struct sr_report_custom_entry *ae = report->auth_entries;
01add4
+
01add4
+    PyObject *auth = PyDict_New();
01add4
+    for (ae = report->auth_entries; ae; ae = ae->next)
01add4
+    {
01add4
+        PyObject *val = PyString_FromString(ae->value);
01add4
+        if (!val)
01add4
+            return NULL;
01add4
+
01add4
+        if (PyDict_SetItemString(auth, ae->key, val) == -1)
01add4
+            return NULL;
01add4
+    }
01add4
+
01add4
+    return auth;
01add4
+}
01add4
+
01add4
+int
01add4
+sr_py_report_set_auth(PyObject *self, PyObject *rhs, void *data)
01add4
+{
01add4
+    PyErr_SetString(PyExc_NotImplementedError, "Setting auth data is not implemented.");
01add4
+    return -1;
01add4
+}
01add4
diff --git a/python/py_report.h b/python/py_report.h
01add4
index a451dbc..88ec30f 100644
01add4
--- a/python/py_report.h
01add4
+++ b/python/py_report.h
01add4
@@ -65,6 +65,8 @@ PyObject *sr_py_report_str(PyObject *self);
01add4
 PyObject *sr_py_report_get_version(PyObject *self, void *data);
01add4
 PyObject *sr_py_report_get_type(PyObject *self, void *data);
01add4
 int sr_py_report_set_type(PyObject *self, PyObject *rhs, void *data);
01add4
+PyObject *sr_py_report_get_auth(PyObject *self, void *data);
01add4
+int sr_py_report_set_auth(PyObject *self, PyObject *rhs, void *data);
01add4
 
01add4
 /**
01add4
  * Methods.
01add4
diff --git a/tests/json_files/ureport-1-auth b/tests/json_files/ureport-1-auth
01add4
new file mode 100644
01add4
index 0000000..a8db2df
01add4
--- /dev/null
01add4
+++ b/tests/json_files/ureport-1-auth
01add4
@@ -0,0 +1,112 @@
01add4
+{
01add4
+  "ureport_version": 2,
01add4
+
01add4
+  "reason": "Program /usr/bin/sleep was terminated by signal 11",
01add4
+
01add4
+  "os": {
01add4
+    "name": "fedora",
01add4
+    "version": "18",
01add4
+    "architecture": "x86_64"
01add4
+  },
01add4
+
01add4
+  "problem": {
01add4
+    "type": "core",
01add4
+
01add4
+    "executable": "/usr/bin/sleep",
01add4
+
01add4
+    "signal": 11,
01add4
+
01add4
+    "component": "coreutils",
01add4
+
01add4
+    "user": {
01add4
+      "local": true,
01add4
+      "root": false
01add4
+    },
01add4
+
01add4
+    "stacktrace": [
01add4
+      {
01add4
+        "crash_thread": true,
01add4
+
01add4
+        "frames": [
01add4
+          {
01add4
+            "build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6",
01add4
+            "build_id_offset": 767024,
01add4
+            "file_name": "/lib64/libc.so.6",
01add4
+            "address": 251315074096,
01add4
+            "fingerprint": "6c1eb9626919a2a5f6a4fc4c2edc9b21b33b7354",
01add4
+            "function_name": "__nanosleep"
01add4
+          },
01add4
+          {
01add4
+            "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32",
01add4
+            "build_id_offset": 16567,
01add4
+            "file_name": "/usr/bin/sleep",
01add4
+            "address": 4210871,
01add4
+            "fingerprint": "d24433b82a2c751fc580f47154823e0bed641a54",
01add4
+            "function_name": "close_stdout"
01add4
+          },
01add4
+          {
01add4
+            "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32",
01add4
+            "build_id_offset": 16202,
01add4
+            "file_name": "/usr/bin/sleep",
01add4
+            "address": 4210506,
01add4
+            "fingerprint": "562719fb960d1c4dbf30c04b3cff37c82acc3d2d",
01add4
+            "function_name": "close_stdout"
01add4
+          },
01add4
+          {
01add4
+            "build_id": "cd379d3bb5d07c96d491712e41c34bcd06b2ce32",
01add4
+            "build_id_offset": 6404,
01add4
+            "fingerprint": "2e8fb95adafe21d035b9bcb9993810fecf4be657",
01add4
+            "file_name": "/usr/bin/sleep",
01add4
+            "address": 4200708
01add4
+          },
01add4
+          {
01add4
+            "build_id": "5f6632d75fd027f5b7b410787f3f06c6bf73eee6",
01add4
+            "build_id_offset": 137733,
01add4
+            "file_name": "/lib64/libc.so.6",
01add4
+            "address": 251314444805,
01add4
+            "fingerprint": "075acda5d3230e115cf7c88597eaba416bdaa6bb",
01add4
+            "function_name": "__libc_start_main"
01add4
+          }
01add4
+        ]
01add4
+      }
01add4
+    ]
01add4
+  },
01add4
+
01add4
+  "packages": [
01add4
+    {
01add4
+      "name": "coreutils",
01add4
+      "epoch": 0,
01add4
+      "version": "8.17",
01add4
+      "architecture": "x86_64",
01add4
+      "package_role": "affected",
01add4
+      "release": "8.fc18",
01add4
+      "install_time": 1371464601
01add4
+    },
01add4
+    {
01add4
+      "name": "glibc",
01add4
+      "epoch": 0,
01add4
+      "version": "2.16",
01add4
+      "architecture": "x86_64",
01add4
+      "release": "31.fc18",
01add4
+      "install_time": 1371464176
01add4
+    },
01add4
+    {
01add4
+      "name": "glibc-common",
01add4
+      "epoch": 0,
01add4
+      "version": "2.16",
01add4
+      "architecture": "x86_64",
01add4
+      "release": "31.fc18",
01add4
+      "install_time": 1371464184
01add4
+    }
01add4
+  ],
01add4
+
01add4
+  "reporter": {
01add4
+    "version": "0.3",
01add4
+    "name": "satyr"
01add4
+  },
01add4
+
01add4
+  "auth": {
01add4
+    "hostname": "localhost",
01add4
+    "machine_id": "0000"
01add4
+  }
01add4
+}
01add4
diff --git a/tests/python/report.py b/tests/python/report.py
01add4
index 83fa76b..5f731b5 100755
01add4
--- a/tests/python/report.py
01add4
+++ b/tests/python/report.py
01add4
@@ -91,6 +91,12 @@ class TestReport(BindingsTestCase):
01add4
     def test_hash(self):
01add4
         self.assertHashable(self.report)
01add4
 
01add4
+    def test_auth(self):
01add4
+        self.assertFalse(self.report.auth)
01add4
+        self.assertRaises(NotImplementedError, self.report.__setattr__, 'auth', {'hostname': 'darkstar'})
01add4
+
01add4
+        report_with_auth = satyr.Report(load_input_contents('../json_files/ureport-1-auth'))
01add4
+        self.assertEqual(report_with_auth.auth, {'hostname': 'localhost', 'machine_id': '0000'})
01add4
 
01add4
 if __name__ == '__main__':
01add4
     unittest.main()
01add4
diff --git a/tests/report.at b/tests/report.at
01add4
index 8736137..1e773d2 100644
01add4
--- a/tests/report.at
01add4
+++ b/tests/report.at
01add4
@@ -57,3 +57,84 @@ int main(void)
01add4
   return 0;
01add4
 }
01add4
 ]])
01add4
+
01add4
+## ------------------ ##
01add4
+## sr_report_add_auth ##
01add4
+## ------------------ ##
01add4
+
01add4
+AT_TESTFUN([sr_report_add_auth],
01add4
+[[
01add4
+#include <assert.h>
01add4
+#include <stdio.h>
01add4
+#include <stdlib.h>
01add4
+#include <string.h>
01add4
+#include "report.h"
01add4
+
01add4
+void check_struct(struct sr_report *report, const char **expected)
01add4
+{
01add4
+    const char **exp_iter = expected;
01add4
+    struct sr_report_custom_entry *cust_iter = report->auth_entries;
01add4
+
01add4
+    while(cust_iter && *exp_iter)
01add4
+    {
01add4
+        fprintf(stdout, "Expected('%s':'%s') vs. Current('%s':'%s')\n",
01add4
+            exp_iter[0], exp_iter[1], cust_iter->key, cust_iter->value);
01add4
+
01add4
+        assert(strcmp(cust_iter->key, exp_iter[0]) == 0 &&
01add4
+               strcmp(cust_iter->value, exp_iter[1]) == 0);
01add4
+
01add4
+        cust_iter = cust_iter->next;
01add4
+        exp_iter += 2;
01add4
+    }
01add4
+
01add4
+    assert(cust_iter == NULL);
01add4
+    assert(*exp_iter == NULL);
01add4
+}
01add4
+
01add4
+void check_json(const char *json, const char **expected)
01add4
+{
01add4
+    const char **exp_iter = expected;
01add4
+    while (*exp_iter)
01add4
+    {
01add4
+        char *entry = NULL;
01add4
+        asprintf(&entry, "\"%s\": \"%s\"", exp_iter[0], exp_iter[1]);
01add4
+
01add4
+        fprintf(stdout, "Checking: '%s'\n", entry);
01add4
+
01add4
+        if (strstr(json, entry) == NULL)
01add4
+        {
01add4
+            fprintf(stderr, "JSON:\n%s\n", json);
01add4
+            abort();
01add4
+        }
01add4
+
01add4
+        exp_iter += 2;
01add4
+    }
01add4
+}
01add4
+
01add4
+
01add4
+int main(void)
01add4
+{
01add4
+  struct sr_report *report = sr_report_new();
01add4
+
01add4
+  sr_report_add_auth(report, "foo", "blah");
01add4
+  sr_report_add_auth(report, "abrt", "awesome");
01add4
+  sr_report_add_auth(report, "satyr", "wonderful");
01add4
+
01add4
+  const char *expected[] = { "satyr", "wonderful", "abrt", "awesome", "foo", "blah", NULL };
01add4
+
01add4
+  check_struct(report, expected);
01add4
+
01add4
+  sr_report_to_json(report);
01add4
+
01add4
+  char *json = sr_report_to_json(report);
01add4
+
01add4
+  check_json(json, expected);
01add4
+
01add4
+  char *error = NULL;
01add4
+  struct sr_report *copy = sr_report_from_json_text(json, &error);
01add4
+
01add4
+  check_struct(copy, expected);
01add4
+
01add4
+  return 0;
01add4
+}
01add4
+]])
01add4
-- 
01add4
1.9.3
01add4