[evolution-data-server] Add a 'secret-monitor' module.



commit b1ec80ce5ef11636adb16aa0f339ec32f404aebf
Author: Matthew Barnes <mbarnes redhat com>
Date:   Thu Feb 13 14:49:17 2014 -0500

    Add a 'secret-monitor' module.
    
    Similar to 'cache-reaper', this periodically scans the set of cached EDS
    passwords and deletes those that have no corresponding ESource.  It also
    updates the password item's label based on the ESource's display name in
    case the user renamed the ESource.

 configure.ac                                   |    1 +
 modules/Makefile.am                            |    1 +
 modules/secret-monitor/Makefile.am             |   30 +++
 modules/secret-monitor/module-secret-monitor.c |  271 ++++++++++++++++++++++++
 4 files changed, 303 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 58d3b3e..98f17b5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1765,6 +1765,7 @@ modules/gnome-online-accounts/Makefile
 modules/google-backend/Makefile
 modules/outlook-backend/Makefile
 modules/owncloud-backend/Makefile
+modules/secret-monitor/Makefile
 modules/ubuntu-online-accounts/Makefile
 modules/ubuntu-online-accounts/calendar.service-type.in
 modules/ubuntu-online-accounts/contacts.service-type.in
diff --git a/modules/Makefile.am b/modules/Makefile.am
index f3a719f..683fc45 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -17,6 +17,7 @@ SUBDIRS = \
        google-backend \
        outlook-backend \
        owncloud-backend \
+       secret-monitor \
        yahoo-backend \
        $(TRUST_PROMPT_DIR) \
        $(GNOME_ONLINE_ACCOUNTS_DIR) \
diff --git a/modules/secret-monitor/Makefile.am b/modules/secret-monitor/Makefile.am
new file mode 100644
index 0000000..557c159
--- /dev/null
+++ b/modules/secret-monitor/Makefile.am
@@ -0,0 +1,30 @@
+NULL =
+
+module_LTLIBRARIES = module-secret-monitor.la
+
+module_secret_monitor_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       -I$(top_srcdir) \
+       -DG_LOG_DOMAIN=\"module-secret-monitor\" \
+       $(E_BACKEND_CFLAGS) \
+       $(E_DATA_SERVER_CFLAGS) \
+       $(LIBSECRET_CFLAGS) \
+       $(NULL)
+
+module_secret_monitor_la_SOURCES = \
+       module-secret-monitor.c \
+       $(NULL)
+
+module_secret_monitor_la_LIBADD = \
+       $(top_builddir)/libebackend/libebackend-1.2.la \
+       $(top_builddir)/libedataserver/libedataserver-1.2.la \
+       $(E_BACKEND_LIBS) \
+       $(E_DATA_SERVER_LIBS) \
+       $(LIBSECRET_LIBS) \
+       $(NULL)
+
+module_secret_monitor_la_LDFLAGS = \
+       -module -avoid-version $(NO_UNDEFINED) \
+       $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/secret-monitor/module-secret-monitor.c b/modules/secret-monitor/module-secret-monitor.c
new file mode 100644
index 0000000..fa1113f
--- /dev/null
+++ b/modules/secret-monitor/module-secret-monitor.c
@@ -0,0 +1,271 @@
+/*
+ * module-secret-monitor.c
+ *
+ * This library is free software you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* XXX Yeah, yeah... */
+#define SECRET_API_SUBJECT_TO_CHANGE
+
+#include <libsecret/secret.h>
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SECRET_MONITOR \
+       (e_secret_monitor_get_type ())
+#define E_SECRET_MONITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SECRET_MONITOR, ESecretMonitor))
+
+/* XXX These intervals are borrowed from the 'cache-reaper' module, and
+ *     are just as arbitrary here as they are there.  On startup we wait
+ *     an hour to scan secrets, and thereafter repeat every 24 hours. */
+#define INITIAL_INTERVAL_SECONDS  ( 1 * (60 * 60))
+#define REGULAR_INTERVAL_SECONDS  (24 * (60 * 60))
+
+typedef struct _ESecretMonitor ESecretMonitor;
+typedef struct _ESecretMonitorClass ESecretMonitorClass;
+
+struct _ESecretMonitor {
+       EExtension parent;
+
+       guint scan_timeout_id;
+};
+
+struct _ESecretMonitorClass {
+       EExtensionClass parent_class;
+};
+
+/* XXX ESource's SecretSchema copied here for searching.
+ *     Maybe add a searching function to e-source.[ch]? */
+
+#define KEYRING_ITEM_ATTRIBUTE_NAME    "e-source-uid"
+#define KEYRING_ITEM_DISPLAY_FORMAT    "Evolution Data Source '%s'"
+
+static SecretSchema password_schema = {
+       "org.gnome.Evolution.Data.Source",
+       SECRET_SCHEMA_DONT_MATCH_NAME,
+       {
+               { KEYRING_ITEM_ATTRIBUTE_NAME,
+                 SECRET_SCHEMA_ATTRIBUTE_STRING },
+               { NULL, 0 }
+       }
+};
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_secret_monitor_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (
+       ESecretMonitor,
+       e_secret_monitor,
+       E_TYPE_EXTENSION)
+
+static ESourceRegistryServer *
+secret_monitor_get_server (ESecretMonitor *extension)
+{
+       EExtensible *extensible;
+
+       extensible = e_extension_get_extensible (E_EXTENSION (extension));
+
+       return E_SOURCE_REGISTRY_SERVER (extensible);
+}
+
+static gpointer
+secret_monitor_scan_secrets_thread (gpointer user_data)
+{
+       ESourceRegistryServer *server;
+       GHashTable *attributes;
+       GList *list, *link;
+       GError *local_error = NULL;
+
+       /* We bail on the first error because 1) this processing is
+        * periodic and not critical, and 2) if a D-Bus call fails,
+        * subsequent D-Bus calls are also likely to fail. */
+
+       server = E_SOURCE_REGISTRY_SERVER (user_data);
+
+       /* XXX secret_service_search_sync() won't accept NULL for its
+        *     'attributes' parameter, so give it an empty hash table. */
+       attributes = g_hash_table_new (g_str_hash, g_str_equal);
+
+       /* List all items under our custom SecretSchema. */
+       list = secret_service_search_sync (
+               NULL, &password_schema, attributes,
+               SECRET_SEARCH_ALL, NULL, &local_error);
+
+       g_hash_table_destroy (attributes);
+
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               SecretItem *item = SECRET_ITEM (link->data);
+               ESource *source;
+               const gchar *uid;
+
+               item = SECRET_ITEM (link->data);
+
+               /* Skip locked items. */
+               if (secret_item_get_locked (item))
+                       continue;
+
+               attributes = secret_item_get_attributes (item);
+
+               uid = g_hash_table_lookup (
+                       attributes, KEYRING_ITEM_ATTRIBUTE_NAME);
+
+               /* No UID attribute?  Best leave it alone. */
+               if (uid == NULL)
+                       continue;
+
+               source = e_source_registry_server_ref_source (server, uid);
+
+               /* If we find a matching ESource, update the SecretItem's
+                * label based on the ESource's display name.  Otherwise,
+                * delete the orphaned SecretItem. */
+               if (source != NULL) {
+                       gchar *new_label;
+                       gchar *old_label;
+
+                       new_label = e_source_dup_secret_label (source);
+                       old_label = secret_item_get_label (item);
+
+                       if (g_strcmp0 (old_label, new_label) != 0) {
+                               secret_item_set_label_sync (
+                                       item, new_label, NULL, &local_error);
+                       }
+
+                       g_free (new_label);
+                       g_free (old_label);
+
+               } else {
+                       secret_item_delete_sync (item, NULL, &local_error);
+               }
+
+               if (local_error != NULL)
+                       break;
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+       if (local_error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, local_error->message);
+               g_error_free (local_error);
+       }
+
+       g_clear_object (&server);
+
+       return NULL;
+}
+
+static gboolean
+secret_monitor_scan_secrets_timeout_cb (gpointer user_data)
+{
+       GThread *thread;
+       ESecretMonitor *extension;
+       ESourceRegistryServer *server;
+       GError *local_error = NULL;
+
+       extension = E_SECRET_MONITOR (user_data);
+       server = secret_monitor_get_server (extension);
+
+       g_debug ("Scanning and pruning saved passwords");
+
+       /* Do the real work in a thread, so we can use synchronous
+        * libsecret calls and keep the logic flow easy to follow. */
+
+       thread = g_thread_try_new (
+               G_LOG_DOMAIN,
+               secret_monitor_scan_secrets_thread,
+               g_object_ref (server), &local_error);
+
+       /* Sanity check. */
+       g_warn_if_fail (
+               ((thread != NULL) && (local_error == NULL)) ||
+               ((thread == NULL) && (local_error != NULL)));
+
+       if (thread != NULL)
+               g_thread_unref (thread);
+
+       if (local_error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, local_error->message);
+               g_error_free (local_error);
+               g_object_unref (server);
+       }
+
+       /* Always explicitly reschedule since the initial
+        * interval is different than the regular interval. */
+       extension->scan_timeout_id = e_named_timeout_add_seconds (
+               REGULAR_INTERVAL_SECONDS,
+               secret_monitor_scan_secrets_timeout_cb,
+               extension);
+
+       return G_SOURCE_REMOVE;
+}
+
+static void
+secret_monitor_finalize (GObject *object)
+{
+       ESecretMonitor *extension;
+
+       extension = E_SECRET_MONITOR (object);
+
+       if (extension->scan_timeout_id > 0)
+               g_source_remove (extension->scan_timeout_id);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_secret_monitor_parent_class)->finalize (object);
+}
+
+static void
+e_secret_monitor_class_init (ESecretMonitorClass *class)
+{
+       GObjectClass *object_class;
+       EExtensionClass *extension_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->finalize = secret_monitor_finalize;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
+}
+
+static void
+e_secret_monitor_class_finalize (ESecretMonitorClass *class)
+{
+}
+
+static void
+e_secret_monitor_init (ESecretMonitor *extension)
+{
+       /* Schedule the initial scan. */
+       extension->scan_timeout_id = e_named_timeout_add_seconds (
+               INITIAL_INTERVAL_SECONDS,
+               secret_monitor_scan_secrets_timeout_cb,
+               extension);
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       e_secret_monitor_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
+


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