PolicyKit: Branch 'master'

David Zeuthen david at kemper.freedesktop.org
Mon Feb 9 18:13:45 PST 2009


 docs/TODO                                       |    4 
 docs/polkit/polkit-docs.xml                     |    1 
 docs/polkitbackend/polkitbackend-sections.txt   |   19 
 docs/polkitbackend/polkitbackend.types          |    1 
 src/nullbackend/50-nullbackend.conf             |   13 
 src/nullbackend/polkitbackendnullauthority.c    |  122 -----
 src/polkitbackend/50-localauthority.conf        |   26 +
 src/polkitbackend/Makefile.am                   |    6 
 src/polkitbackend/polkitbackend.h               |    1 
 src/polkitbackend/polkitbackendconfigsource.c   |  563 ++++++++++++++++++++++++
 src/polkitbackend/polkitbackendconfigsource.h   |   97 ++++
 src/polkitbackend/polkitbackendlocalauthority.c |  149 +++++-
 src/polkitbackend/polkitbackendtypes.h          |    2 
 13 files changed, 868 insertions(+), 136 deletions(-)

New commits:
commit 2d65587fbf21bcaacce05798533ffd0900e132d9
Author: David Zeuthen <davidz at redhat.com>
Date:   Mon Feb 9 21:10:46 2009 -0500

    make it possible to define what administrator authentication means
    
    We might also want to expose methods on the D-Bus interface
    org.freedesktop.PolicyKit1.AuthorityManager to configure this;
    implementation-wise we'd just be writing out config files with a fixed
    priority.

diff --git a/docs/TODO b/docs/TODO
index 36c1eec..2230711 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -22,10 +22,6 @@ Core TODO items
  - make sure simple operations work when no system bus is present
    - e.g. %post RPM scripts adding/removing authorizations to identities
 
- - for administrator authentication, make it possible to use 'wheel' group
-   sudo-style authentication (e.g. select one or more identities that the
-   user can choose to authenticate as)
-
  - maybe use file monitors on /var/lib/polkit-1 directories and
    emit the Changed() signal
 
diff --git a/docs/polkit/polkit-docs.xml b/docs/polkit/polkit-docs.xml
index 5cdb817..75e7620 100644
--- a/docs/polkit/polkit-docs.xml
+++ b/docs/polkit/polkit-docs.xml
@@ -90,6 +90,7 @@
     <xi:include href="../polkitbackend/xml/polkitbackendlocalauthority.xml"/>
     <xi:include href="../polkitbackend/xml/polkitbackendactionpool.xml"/>
     <xi:include href="../polkitbackend/xml/polkitbackendsessionmonitor.xml"/>
+    <xi:include href="../polkitbackend/xml/polkitbackendconfigsource.xml"/>
   </reference>
 
   <reference id="ref-authentication-agent-api">
diff --git a/docs/polkitbackend/polkitbackend-sections.txt b/docs/polkitbackend/polkitbackend-sections.txt
index 1c8f925..e370b96 100644
--- a/docs/polkitbackend/polkitbackend-sections.txt
+++ b/docs/polkitbackend/polkitbackend-sections.txt
@@ -81,3 +81,22 @@ POLKIT_BACKEND_SESSION_MONITOR_CLASS
 POLKIT_BACKEND_IS_SESSION_MONITOR_CLASS
 POLKIT_BACKEND_SESSION_MONITOR_GET_CLASS
 </SECTION>
+
+<SECTION>
+<FILE>polkitbackendconfigsource</FILE>
+PolkitBackendConfigSource
+polkit_backend_config_source_new
+polkit_backend_config_source_get_integer
+polkit_backend_config_source_get_boolean
+polkit_backend_config_source_get_double
+polkit_backend_config_source_get_string
+polkit_backend_config_source_get_string_list
+<SUBSECTION Standard>
+POLKIT_BACKEND_CONFIG_SOURCE
+POLKIT_BACKEND_IS_CONFIG_SOURCE
+POLKIT_BACKEND_TYPE_CONFIG_SOURCE
+polkit_backend_config_source_get_type
+POLKIT_BACKEND_CONFIG_SOURCE_CLASS
+POLKIT_BACKEND_IS_CONFIG_SOURCE_CLASS
+POLKIT_BACKEND_CONFIG_SOURCE_GET_CLASS
+</SECTION>
diff --git a/docs/polkitbackend/polkitbackend.types b/docs/polkitbackend/polkitbackend.types
index 9b16c54..248f48b 100644
--- a/docs/polkitbackend/polkitbackend.types
+++ b/docs/polkitbackend/polkitbackend.types
@@ -2,3 +2,4 @@ polkit_backend_authority_get_type
 polkit_backend_local_authority_get_type
 polkit_backend_action_pool_get_type
 polkit_backend_session_monitor_get_type
+polkit_backend_config_source_get_type
diff --git a/src/nullbackend/50-nullbackend.conf b/src/nullbackend/50-nullbackend.conf
index 1c91137..3497677 100644
--- a/src/nullbackend/50-nullbackend.conf
+++ b/src/nullbackend/50-nullbackend.conf
@@ -1,15 +1,16 @@
 #
 # Configuration file for the PolicyKit null backend.
 #
-# Do not edit this file, it will be overwritten on update.
+# DO NOT EDIT THIS FILE, it will be overwritten on update.
 #
 # To change configuration, create another file in this directory with
-# a filename that is sorted after the 50-nullback.conf.
+# a filename that is sorted after the 50-nullback.conf and make
+# sure it has the .conf extension.
 #
-# Only a single configuration item is supported now. The priority of
-# the null backend. It defaults to -10. See the PolicyKit documentation
-# for more information about PolicyKit backends.
+# Only a single configuration item, Priority, is supported.
+#
+# See the PolicyKit documentation for more information about PolicyKit.
 #
 
 [Configuration]
-priority=-10
+Priority=-10
diff --git a/src/nullbackend/polkitbackendnullauthority.c b/src/nullbackend/polkitbackendnullauthority.c
index 543d2cd..f754bd5 100644
--- a/src/nullbackend/polkitbackendnullauthority.c
+++ b/src/nullbackend/polkitbackendnullauthority.c
@@ -94,126 +94,17 @@ polkit_backend_null_authority_class_finalize (PolkitBackendNullAuthorityClass *k
 {
 }
 
-static gint
-compare_filename (GFile *a, GFile *b)
-{
-  gchar *a_uri;
-  gchar *b_uri;
-  gint ret;
-
-  a_uri = g_file_get_uri (a);
-  b_uri = g_file_get_uri (b);
-
-  ret = g_strcmp0 (a_uri, b_uri);
-
-  return ret;
-}
-
-/* Loads and process all .conf files in /etc/polkit-1/nullbackend.conf.d/ in order */
-static void
-load_config (gint *out_priority)
-{
-  GFileEnumerator *enumerator;
-  GFile *directory;
-  GFileInfo *file_info;
-  GError *error;
-  GList *files;
-  GList *l;
-
-  directory = g_file_new_for_path (PACKAGE_SYSCONF_DIR "/polkit-1/nullbackend.conf.d");
-
-  files = NULL;
-
-  error = NULL;
-  enumerator = g_file_enumerate_children (directory,
-                                          "standard::*",
-                                          G_FILE_QUERY_INFO_NONE,
-                                          NULL,
-                                          &error);
-  if (error != NULL)
-    {
-      g_warning ("Error enumerating files: %s", error->message);
-      goto out;
-    }
-
-  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)
-    {
-      const gchar *name;
-
-      name = g_file_info_get_name (file_info);
-
-      /* only consider files ending in .conf */
-      if (g_str_has_suffix (name, ".conf"))
-        files = g_list_prepend (files, g_file_get_child (directory, name));
-
-      g_object_unref (file_info);
-    }
-  if (error != NULL)
-    {
-      g_warning ("Error enumerating files: %s", error->message);
-      goto out;
-    }
-  g_object_unref (enumerator);
-
-  files = g_list_sort (files, (GCompareFunc) compare_filename);
-
-  for (l = files; l != NULL; l = l->next)
-    {
-      GFile *file = G_FILE (l->data);
-      gchar *filename;
-      GKeyFile *key_file;
-
-      filename = g_file_get_path (file);
-
-      key_file = g_key_file_new ();
-      error = NULL;
-      if (!g_key_file_load_from_file (key_file,
-                                      filename,
-                                      G_KEY_FILE_NONE,
-                                      NULL))
-        {
-          g_warning ("Error loading file %s: %s", filename, error->message);
-          g_error_free (error);
-          error = NULL;
-        }
-      else
-        {
-          gint priority;
-
-          priority = g_key_file_get_integer (key_file,
-                                             "Configuration",
-                                             "priority",
-                                             &error);
-          if (error != NULL)
-            {
-              /* don't warn, not all config files may have this key */
-              g_error_free (error);
-            }
-          else
-            {
-              *out_priority = priority;
-            }
-
-          g_key_file_free (key_file);
-        }
-
-      g_free (filename);
-    }
-
- out:
-  g_object_unref (directory);
-  g_list_foreach (files, (GFunc) g_object_unref, NULL);
-  g_list_free (files);
-}
-
 void
 polkit_backend_null_authority_register (GIOModule *module)
 {
   gint priority;
+  GFile *directory;
+  PolkitBackendConfigSource *source;
 
-  priority = -1;
+  directory = g_file_new_for_path (PACKAGE_SYSCONF_DIR "/polkit-1/nullbackend.conf.d");
+  source = polkit_backend_config_source_new (directory);
 
-  load_config (&priority);
+  priority = polkit_backend_config_source_get_integer (source, "Configuration", "Priority", NULL);
 
   polkit_backend_null_authority_register_type (G_TYPE_MODULE (module));
 
@@ -223,6 +114,9 @@ polkit_backend_null_authority_register (GIOModule *module)
                                   POLKIT_BACKEND_TYPE_NULL_AUTHORITY,
                                   "null backend " PACKAGE_VERSION,
                                   priority);
+
+  g_object_unref (directory);
+  g_object_unref (source);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/polkitbackend/50-localauthority.conf b/src/polkitbackend/50-localauthority.conf
new file mode 100644
index 0000000..4514828
--- /dev/null
+++ b/src/polkitbackend/50-localauthority.conf
@@ -0,0 +1,26 @@
+#
+# Configuration file for the PolicyKit local authority backend.
+#
+# DO NOT EDIT THIS FILE, it will be overwritten on update.
+#
+# To change configuration, create another file in this directory with
+# a filename that is sorted after the 50-localauthority.conf and make
+# sure it has the .conf extension.
+#
+# Only a single configuration item, AdminIdentities, is supported. It
+# specifies what set of identities to use for administrator authentication.
+# The value is a semicolon-separated list of identities. An identity can
+# be specified as
+#
+#  unix-user:<user>
+#  unix-group:<group>
+#
+# where <user> is username/uid and <group> is a groupname/gid. When
+# using the group directive, any user in the given group can chosen
+# for authentication.
+#
+# See the PolicyKit documentation for more information about PolicyKit.
+#
+
+[Configuration]
+AdminIdentities=unix-user:0
diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am
index 4714856..51ebe3c 100644
--- a/src/polkitbackend/Makefile.am
+++ b/src/polkitbackend/Makefile.am
@@ -42,6 +42,7 @@ libpolkit_backend_1include_HEADERS =                        		\
 	polkitbackendlocalauthority.h					\
 	polkitbackendactionpool.h					\
 	polkitbackendsessionmonitor.h					\
+	polkitbackendconfigsource.h					\
         $(NULL)
 
 libpolkit_backend_1_la_SOURCES =                                   	\
@@ -54,6 +55,7 @@ libpolkit_backend_1_la_SOURCES =                                   	\
 	polkitbackendlocalauthority.h	polkitbackendlocalauthority.c	\
 	polkitbackendactionpool.h	polkitbackendactionpool.c	\
 	polkitbackendsessionmonitor.h	polkitbackendsessionmonitor.c	\
+	polkitbackendconfigsource.h	polkitbackendconfigsource.c	\
         $(NULL)
 
 libpolkit_backend_1_la_CFLAGS =                                        	\
@@ -72,8 +74,12 @@ libpolkit_backend_1_la_LIBADD =                               		\
 
 CLEANFILES = $(BUILT_SOURCES)
 
+localauthorityconfigdir = $(sysconfdir)/polkit-1/localauthority.conf.d
+localauthorityconfig_DATA = 50-localauthority.conf
+
 EXTRA_DIST =								\
 	org.freedesktop.ConsoleKit.xml					\
+	$(localauthorityconfig_DATA)					\
 	$(NULL)
 
 dist-hook :
diff --git a/src/polkitbackend/polkitbackend.h b/src/polkitbackend/polkitbackend.h
index b6dcc1d..57a9842 100644
--- a/src/polkitbackend/polkitbackend.h
+++ b/src/polkitbackend/polkitbackend.h
@@ -34,6 +34,7 @@
 #include <polkitbackend/polkitbackendlocalauthority.h>
 #include <polkitbackend/polkitbackendactionpool.h>
 #include <polkitbackend/polkitbackendsessionmonitor.h>
+#include <polkitbackend/polkitbackendconfigsource.h>
 #undef _POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H
 
 #endif /* __POLKIT_BACKEND_H */
diff --git a/src/polkitbackend/polkitbackendconfigsource.c b/src/polkitbackend/polkitbackendconfigsource.c
new file mode 100644
index 0000000..348b3e1
--- /dev/null
+++ b/src/polkitbackend/polkitbackendconfigsource.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz at redhat.com>
+ */
+
+#include "config.h"
+
+#include <polkit/polkit.h>
+#include "polkitbackendconfigsource.h"
+
+/**
+ * SECTION:polkitbackendconfigsource
+ * @title: PolkitBackendConfigSource
+ * @short_description: Access configuration files
+ *
+ * The #PolkitBackendConfigSource is a utility class to read configuration data
+ * from a set of prioritized key-value files in a given directory.
+ */
+
+struct _PolkitBackendConfigSourcePrivate
+{
+  GFile *directory;
+
+  GFileMonitor *directory_monitor;
+
+  /* sorted according to priority, higher priority is first */
+  GList *key_files;
+
+  gboolean has_data;
+};
+
+enum
+{
+  PROP_0,
+  PROP_DIRECTORY,
+};
+
+enum
+{
+  CHANGED_SIGNAL,
+  LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+static void polkit_backend_config_source_purge (PolkitBackendConfigSource *source);
+
+static void polkit_backend_config_source_ensure (PolkitBackendConfigSource *source);
+
+G_DEFINE_TYPE (PolkitBackendConfigSource, polkit_backend_config_source, G_TYPE_OBJECT);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+polkit_backend_config_source_init (PolkitBackendConfigSource *source)
+{
+  source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source,
+                                              POLKIT_BACKEND_TYPE_CONFIG_SOURCE,
+                                              PolkitBackendConfigSourcePrivate);
+}
+
+static void
+polkit_backend_config_source_finalize (GObject *object)
+{
+  PolkitBackendConfigSource *source = POLKIT_BACKEND_CONFIG_SOURCE (object);
+
+  if (source->priv->directory != NULL)
+    g_object_unref (source->priv->directory);
+
+  if (source->priv->directory_monitor != NULL)
+    g_object_unref (source->priv->directory_monitor);
+
+  g_list_foreach (source->priv->key_files, (GFunc) g_key_file_free, NULL);
+  g_list_free (source->priv->key_files);
+
+  if (G_OBJECT_CLASS (polkit_backend_config_source_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (polkit_backend_config_source_parent_class)->finalize (object);
+}
+
+
+static void
+polkit_backend_config_source_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+  PolkitBackendConfigSource *source = POLKIT_BACKEND_CONFIG_SOURCE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DIRECTORY:
+      g_value_set_object (value, source->priv->directory);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+polkit_backend_config_source_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+  PolkitBackendConfigSource *source = POLKIT_BACKEND_CONFIG_SOURCE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DIRECTORY:
+      source->priv->directory = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+directory_monitor_changed (GFileMonitor     *monitor,
+                           GFile            *file,
+                           GFile            *other_file,
+                           GFileMonitorEvent event_type,
+                           gpointer          user_data)
+{
+  PolkitBackendConfigSource *source;
+
+  source = POLKIT_BACKEND_CONFIG_SOURCE (user_data);
+
+  if (file != NULL)
+    {
+      gchar *name;
+
+      name = g_file_get_basename (file);
+
+      //g_debug ("event_type=%d file=%p name=%s", event_type, file, name);
+
+      if (!g_str_has_prefix (name, ".") &&
+          !g_str_has_prefix (name, "#") &&
+          g_str_has_suffix (name, ".conf") &&
+          (event_type == G_FILE_MONITOR_EVENT_CREATED ||
+           event_type == G_FILE_MONITOR_EVENT_DELETED ||
+           event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
+        {
+
+          //g_debug ("match");
+
+          /* now throw away all caches */
+          polkit_backend_config_source_purge (source);
+          g_signal_emit_by_name (source, "changed");
+        }
+
+      g_free (name);
+    }
+}
+
+static void
+polkit_backend_config_source_constructed (GObject *object)
+{
+  PolkitBackendConfigSource *source = POLKIT_BACKEND_CONFIG_SOURCE (object);
+  GError *error;
+
+  error = NULL;
+  source->priv->directory_monitor = g_file_monitor_directory (source->priv->directory,
+                                                              G_FILE_MONITOR_NONE,
+                                                              NULL,
+                                                              &error);
+  if (source->priv->directory_monitor == NULL)
+    {
+      gchar *dir_name;
+      dir_name = g_file_get_uri (source->priv->directory);
+      g_warning ("Error monitoring directory %s: %s", dir_name, error->message);
+      g_free (dir_name);
+      g_error_free (error);
+    }
+  else
+    {
+      g_signal_connect (source->priv->directory_monitor,
+                        "changed",
+                        (GCallback) directory_monitor_changed,
+                        source);
+    }
+
+  if (G_OBJECT_CLASS (polkit_backend_config_source_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (polkit_backend_config_source_parent_class)->constructed (object);
+}
+
+static void
+polkit_backend_config_source_class_init (PolkitBackendConfigSourceClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->get_property = polkit_backend_config_source_get_property;
+  gobject_class->set_property = polkit_backend_config_source_set_property;
+  gobject_class->finalize     = polkit_backend_config_source_constructed;
+  gobject_class->finalize     = polkit_backend_config_source_finalize;
+
+  g_type_class_add_private (klass, sizeof (PolkitBackendConfigSourcePrivate));
+
+  /**
+   * PolkitBackendConfigSource:directory:
+   *
+   * The directory to watch for configuration files.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DIRECTORY,
+                                   g_param_spec_object ("directory",
+                                                        "Directory",
+                                                        "The directory to watch for configuration files",
+                                                        G_TYPE_FILE,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * PolkitBackendConfiguSource::changed:
+   * @source: A #PolkitBackendConfigSource.
+   *
+   * Emitted when configuration files in #PolkitBackendConfiguSource:directory changes.
+   */
+  signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                                          POLKIT_BACKEND_TYPE_CONFIG_SOURCE,
+                                          G_SIGNAL_RUN_LAST,
+                                          G_STRUCT_OFFSET (PolkitBackendConfigSourceClass, changed),
+                                          NULL,
+                                          NULL,
+                                          g_cclosure_marshal_VOID__VOID,
+                                          G_TYPE_NONE,
+                                          0);
+}
+
+/**
+ * polkit_backend_config_source_new:
+ * @directory: The directory to watch.
+ *
+ * Creates a new #PolkitBackendConfigSource object that reads
+ * configuration from @directory. To watch for configuration changes,
+ * connect to the #PolkitBackendConfigSource::changed signal.
+ *
+ * Returns: A #PolkitBackendConfigSource for @directory. Free with
+ * g_object_unref().
+ **/
+PolkitBackendConfigSource *
+polkit_backend_config_source_new (GFile *directory)
+{
+  PolkitBackendConfigSource *source;
+
+  source = POLKIT_BACKEND_CONFIG_SOURCE (g_object_new (POLKIT_BACKEND_TYPE_CONFIG_SOURCE,
+                                                       "directory", directory,
+                                                       NULL));
+
+  return source;
+}
+
+static void
+polkit_backend_config_source_purge (PolkitBackendConfigSource *source)
+{
+  g_list_foreach (source->priv->key_files, (GFunc) g_key_file_free, NULL);
+  g_list_free (source->priv->key_files);
+  source->priv->key_files = NULL;
+
+  source->priv->has_data = FALSE;
+}
+
+static gint
+compare_filename (GFile *a, GFile *b)
+{
+  gchar *a_uri;
+  gchar *b_uri;
+  gint ret;
+
+  a_uri = g_file_get_uri (a);
+  b_uri = g_file_get_uri (b);
+
+  /* TODO: use ASCII sort function? */
+  ret = -g_strcmp0 (a_uri, b_uri);
+
+  return ret;
+}
+
+static void
+polkit_backend_config_source_ensure (PolkitBackendConfigSource *source)
+{
+  GFileEnumerator *enumerator;
+  GFileInfo *file_info;
+  GError *error;
+  GList *files;
+  GList *l;
+
+  files = NULL;
+
+  if (source->priv->has_data)
+    goto out;
+
+  polkit_backend_config_source_purge (source);
+
+  error = NULL;
+  enumerator = g_file_enumerate_children (source->priv->directory,
+                                          "standard::*",
+                                          G_FILE_QUERY_INFO_NONE,
+                                          NULL,
+                                          &error);
+  if (enumerator == NULL)
+    {
+      gchar *dir_name;
+      dir_name = g_file_get_uri (source->priv->directory);
+      g_warning ("Error enumerating files in %s: %s", dir_name, error->message);
+      g_free (dir_name);
+      g_error_free (error);
+      goto out;
+    }
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)
+    {
+      const gchar *name;
+
+      name = g_file_info_get_name (file_info);
+
+      /* only consider files ending in .conf */
+      if (g_str_has_suffix (name, ".conf"))
+        files = g_list_prepend (files, g_file_get_child (source->priv->directory, name));
+
+      g_object_unref (file_info);
+    }
+  g_object_unref (enumerator);
+  if (error != NULL)
+    {
+      g_warning ("Error enumerating files: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  files = g_list_sort (files, (GCompareFunc) compare_filename);
+
+  /* process files; highest priority comes first */
+  for (l = files; l != NULL; l = l->next)
+    {
+      GFile *file = G_FILE (l->data);
+      gchar *filename;
+      GKeyFile *key_file;
+
+      filename = g_file_get_path (file);
+
+      key_file = g_key_file_new ();
+
+      error = NULL;
+      if (!g_key_file_load_from_file (key_file,
+                                      filename,
+                                      G_KEY_FILE_NONE,
+                                      NULL))
+        {
+          g_warning ("Error loading key-file %s: %s", filename, error->message);
+          g_error_free (error);
+          error = NULL;
+          g_key_file_free (key_file);
+        }
+      else
+        {
+          source->priv->key_files = g_list_prepend (source->priv->key_files, key_file);
+        }
+
+      g_free (filename);
+    }
+
+  source->priv->key_files = g_list_reverse (source->priv->key_files);
+
+ out:
+  g_list_foreach (files, (GFunc) g_object_unref, NULL);
+  g_list_free (files);
+}
+
+static GKeyFile *
+find_key_file (PolkitBackendConfigSource  *source,
+               const gchar                *group,
+               const gchar                *key,
+               GError                    **error)
+{
+  GList *l;
+  GKeyFile *ret;
+
+  ret = NULL;
+
+  for (l = source->priv->key_files; l != NULL; l = l->next)
+    {
+      GKeyFile *key_file = l->data;
+
+      if (g_key_file_has_key (key_file, group, key, NULL))
+        {
+          ret = key_file;
+          goto out;
+        }
+    }
+
+ out:
+  if (ret == NULL)
+    g_set_error_literal (error,
+                         G_KEY_FILE_ERROR,
+                         G_KEY_FILE_ERROR_NOT_FOUND,
+                         "Group/Key combo not found in any config file");
+  return ret;
+}
+
+/**
+ * polkit_backend_config_source_get_integer:
+ * @source: A PolkitBackendConfigSource.
+ * @group: A group name.
+ * @key: A key name.
+ * @error: Return location for error or %NULL.
+ *
+ * Gets the value associated with @key under @group_name.
+ *
+ * Returns: The value or 0 if @error is set.
+ **/
+gint
+polkit_backend_config_source_get_integer (PolkitBackendConfigSource  *source,
+                                          const gchar                *group,
+                                          const gchar                *key,
+                                          GError                    **error)
+{
+  GKeyFile *key_file;
+
+  polkit_backend_config_source_ensure (source);
+
+  key_file = find_key_file (source, group, key, error);
+  if (key_file == NULL)
+    return 0;
+
+  return g_key_file_get_integer (key_file, group, key, error);
+}
+
+/**
+ * polkit_backend_config_source_get_boolean:
+ * @source: A PolkitBackendConfigSource.
+ * @group: A group name.
+ * @key: A key name.
+ * @error: Return location for error or %NULL.
+ *
+ * Gets the value associated with @key under @group_name.
+ *
+ * Returns: The value or %FALSE if @error is set.
+ **/
+gboolean
+polkit_backend_config_source_get_boolean (PolkitBackendConfigSource  *source,
+                                          const gchar                *group,
+                                          const gchar                *key,
+                                          GError                    **error)
+{
+  GKeyFile *key_file;
+
+  polkit_backend_config_source_ensure (source);
+
+  key_file = find_key_file (source, group, key, error);
+  if (key_file == NULL)
+    return FALSE;
+
+  return g_key_file_get_boolean (key_file, group, key, error);
+}
+
+/**
+ * polkit_backend_config_source_get_double:
+ * @source: A PolkitBackendConfigSource.
+ * @group: A group name.
+ * @key: A key name.
+ * @error: Return location for error or %NULL.
+ *
+ * Gets the value associated with @key under @group_name.
+ *
+ * Returns: The value or 0.0 if @error is set.
+ **/
+gdouble
+polkit_backend_config_source_get_double (PolkitBackendConfigSource  *source,
+                                         const gchar                *group,
+                                         const gchar                *key,
+                                         GError                    **error)
+{
+  GKeyFile *key_file;
+
+  polkit_backend_config_source_ensure (source);
+
+  key_file = find_key_file (source, group, key, error);
+  if (key_file == NULL)
+    return 0.0;
+
+  return g_key_file_get_double (key_file, group, key, error);
+}
+
+/**
+ * polkit_backend_config_source_get_string:
+ * @source: A PolkitBackendConfigSource.
+ * @group: A group name.
+ * @key: A key name.
+ * @error: Return location for error or %NULL.
+ *
+ * Gets the value associated with @key under @group_name.
+ *
+ * Returns: The value or %NULL if @error is set.
+ **/
+gchar *
+polkit_backend_config_source_get_string (PolkitBackendConfigSource  *source,
+                                         const gchar                *group,
+                                         const gchar                *key,
+                                         GError                    **error)
+{
+  GKeyFile *key_file;
+
+  polkit_backend_config_source_ensure (source);
+
+  key_file = find_key_file (source, group, key, error);
+  if (key_file == NULL)
+    return NULL;
+
+  return g_key_file_get_string (key_file, group, key, error);
+}
+
+/**
+ * polkit_backend_config_source_get_string_list:
+ * @source: A PolkitBackendConfigSource.
+ * @group: A group name.
+ * @key: A key name.
+ * @error: Return location for error or %NULL.
+ *
+ * Gets the values associated with @key under @group_name.
+ *
+ * Returns: The value or %NULL if @error is set.
+ **/
+gchar **
+polkit_backend_config_source_get_string_list (PolkitBackendConfigSource  *source,
+                                              const gchar                *group,
+                                              const gchar                *key,
+                                              GError                    **error)
+{
+  GKeyFile *key_file;
+
+  polkit_backend_config_source_ensure (source);
+
+  key_file = find_key_file (source, group, key, error);
+  if (key_file == NULL)
+    return NULL;
+
+  return g_key_file_get_string_list (key_file, group, key, NULL, error);
+}
diff --git a/src/polkitbackend/polkitbackendconfigsource.h b/src/polkitbackend/polkitbackendconfigsource.h
new file mode 100644
index 0000000..f83a377
--- /dev/null
+++ b/src/polkitbackend/polkitbackendconfigsource.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz at redhat.com>
+ */
+
+#if !defined (_POLKIT_BACKEND_COMPILATION) && !defined(_POLKIT_BACKEND_INSIDE_POLKIT_BACKEND_H)
+#error "Only <polkitbackend/polkitbackend.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef __POLKIT_BACKEND_CONFIG_SOURCE_H
+#define __POLKIT_BACKEND_CONFIG_SOURCE_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <polkitbackend/polkitbackendtypes.h>
+
+G_BEGIN_DECLS
+
+#define POLKIT_BACKEND_TYPE_CONFIG_SOURCE         (polkit_backend_config_source_get_type ())
+#define POLKIT_BACKEND_CONFIG_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_CONFIG_SOURCE, PolkitBackendConfigSource))
+#define POLKIT_BACKEND_CONFIG_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_BACKEND_TYPE_CONFIG_SOURCE, PolkitBackendConfigSourceClass))
+#define POLKIT_BACKEND_CONFIG_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_BACKEND_TYPE_CONFIG_SOURCE,PolkitBackendConfigSourceClass))
+#define POLKIT_BACKEND_IS_CONFIG_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_CONFIG_SOURCE))
+#define POLKIT_BACKEND_IS_CONFIG_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_BACKEND_TYPE_CONFIG_SOURCE))
+
+typedef struct _PolkitBackendConfigSourceClass    PolkitBackendConfigSourceClass;
+typedef struct _PolkitBackendConfigSourcePrivate  PolkitBackendConfigSourcePrivate;
+
+struct _PolkitBackendConfigSource
+{
+  GObject parent_instance;
+  PolkitBackendConfigSourcePrivate *priv;
+};
+
+struct _PolkitBackendConfigSourceClass
+{
+  /*< public >*/
+  GObjectClass parent_class;
+
+  /* Signals */
+  void (*changed)  (PolkitBackendConfigSource *config_source);
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_polkit_reserved1) (void);
+  void (*_polkit_reserved2) (void);
+  void (*_polkit_reserved3) (void);
+  void (*_polkit_reserved4) (void);
+  void (*_polkit_reserved5) (void);
+  void (*_polkit_reserved6) (void);
+  void (*_polkit_reserved7) (void);
+  void (*_polkit_reserved8) (void);
+};
+
+GType                      polkit_backend_config_source_get_type        (void) G_GNUC_CONST;
+PolkitBackendConfigSource *polkit_backend_config_source_new             (GFile                      *directory);
+gint                       polkit_backend_config_source_get_integer     (PolkitBackendConfigSource  *source,
+                                                                         const gchar                *group,
+                                                                         const gchar                *key,
+                                                                         GError                    **error);
+gboolean                   polkit_backend_config_source_get_boolean     (PolkitBackendConfigSource  *source,
+                                                                         const gchar                *group,
+                                                                         const gchar                *key,
+                                                                         GError                    **error);
+gdouble                    polkit_backend_config_source_get_double      (PolkitBackendConfigSource  *source,
+                                                                         const gchar                *group,
+                                                                         const gchar                *key,
+                                                                         GError                    **error);
+gchar                     *polkit_backend_config_source_get_string      (PolkitBackendConfigSource  *source,
+                                                                         const gchar                *group,
+                                                                         const gchar                *key,
+                                                                         GError                    **error);
+gchar                    **polkit_backend_config_source_get_string_list (PolkitBackendConfigSource  *source,
+                                                                         const gchar                *group,
+                                                                         const gchar                *key,
+                                                                         GError                    **error);
+
+G_END_DECLS
+
+#endif /* __POLKIT_BACKEND_CONFIG_SOURCE_H */
+
diff --git a/src/polkitbackend/polkitbackendlocalauthority.c b/src/polkitbackend/polkitbackendlocalauthority.c
index b0d4063..7bbbe2d 100644
--- a/src/polkitbackend/polkitbackendlocalauthority.c
+++ b/src/polkitbackend/polkitbackendlocalauthority.c
@@ -30,6 +30,7 @@
 #include "polkitbackendlocalauthority.h"
 #include "polkitbackendactionpool.h"
 #include "polkitbackendsessionmonitor.h"
+#include "polkitbackendconfigsource.h"
 
 #include <polkit/polkitprivate.h>
 
@@ -49,6 +50,8 @@ typedef struct
 
   PolkitBackendSessionMonitor *session_monitor;
 
+  PolkitBackendConfigSource *config_source;
+
   GHashTable *hash_identity_to_authority_store;
 
   GHashTable *hash_session_to_authentication_agent;
@@ -117,6 +120,10 @@ static gboolean check_temporary_authorization_for_identity (PolkitBackendLocalAu
                                                            PolkitSubject               *subject,
                                                            const gchar                 *action_id);
 
+static GList *get_users_in_group (PolkitBackendLocalAuthority *authority,
+                                  PolkitIdentity              *group,
+                                  gboolean                     include_root);
+
 static GList *get_groups_for_user (PolkitBackendLocalAuthority *authority,
                                    PolkitIdentity              *user);
 
@@ -244,19 +251,22 @@ static void
 polkit_backend_local_authority_init (PolkitBackendLocalAuthority *authority)
 {
   PolkitBackendLocalAuthorityPrivate *priv;
-  GFile *action_desc_directory;
+  GFile *directory;
 
   priv = POLKIT_BACKEND_LOCAL_AUTHORITY_GET_PRIVATE (authority);
 
-  action_desc_directory = g_file_new_for_path (PACKAGE_DATA_DIR "/polkit-1/actions");
-  priv->action_pool = polkit_backend_action_pool_new (action_desc_directory);
-  g_object_unref (action_desc_directory);
-
+  directory = g_file_new_for_path (PACKAGE_DATA_DIR "/polkit-1/actions");
+  priv->action_pool = polkit_backend_action_pool_new (directory);
+  g_object_unref (directory);
   g_signal_connect (priv->action_pool,
                     "changed",
                     (GCallback) action_pool_changed,
                     authority);
 
+  directory = g_file_new_for_path (PACKAGE_SYSCONF_DIR "/polkit-1/localauthority.conf.d");
+  priv->config_source = polkit_backend_config_source_new (directory);
+  g_object_unref (directory);
+
   priv->hash_identity_to_authority_store = g_hash_table_new_full ((GHashFunc) polkit_identity_hash,
                                                                   (GEqualFunc) polkit_identity_equal,
                                                                   (GDestroyNotify) g_object_unref,
@@ -282,6 +292,9 @@ polkit_backend_local_authority_finalize (GObject *object)
   if (priv->action_pool != NULL)
     g_object_unref (priv->action_pool);
 
+  if (priv->config_source != NULL)
+    g_object_unref (priv->config_source);
+
   if (priv->session_monitor != NULL)
     g_object_unref (priv->session_monitor);
 
@@ -1694,6 +1707,67 @@ authentication_agent_begin_callback (GObject *source_object,
   authentication_session_free (session);
 }
 
+static GList *
+get_admin_auth_identities (PolkitBackendLocalAuthority *authority)
+{
+  PolkitBackendLocalAuthorityPrivate *priv;
+  GList *ret;
+  guint n;
+  gchar **admin_identities;
+  GError *error;
+
+  priv = POLKIT_BACKEND_LOCAL_AUTHORITY_GET_PRIVATE (authority);
+
+  error = NULL;
+  admin_identities = polkit_backend_config_source_get_string_list (priv->config_source,
+                                                                   "Configuration",
+                                                                   "AdminIdentities",
+                                                                   &error);
+  if (admin_identities == NULL)
+    {
+      g_warning ("Error getting admin_identities configuration item: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  for (n = 0; admin_identities[n] != NULL; n++)
+    {
+      PolkitIdentity *identity;
+
+      error = NULL;
+      identity = polkit_identity_from_string (admin_identities[n], &error);
+      if (identity == NULL)
+        {
+          g_warning ("Error parsing identity %s: %s", admin_identities[n], error->message);
+          g_error_free (error);
+          continue;
+        }
+
+      if (POLKIT_IS_UNIX_USER (identity))
+        {
+          ret = g_list_append (ret, identity);
+        }
+      else if (POLKIT_IS_UNIX_GROUP (identity))
+        {
+          ret = g_list_concat (ret, get_users_in_group (authority, identity, FALSE));
+        }
+      else
+        {
+          g_warning ("Unsupported identity %s", admin_identities[n]);
+        }
+    }
+
+  g_strfreev (admin_identities);
+
+ out:
+
+  /* default to uid 0 if no admin identities has been found */
+  if (ret == NULL)
+    ret = g_list_prepend (ret, polkit_unix_user_new (0));
+
+  return ret;
+}
+
 static void
 authentication_agent_initiate_challenge (AuthenticationAgent         *agent,
                                          PolkitSubject               *subject,
@@ -1715,14 +1789,18 @@ authentication_agent_initiate_challenge (AuthenticationAgent         *agent,
 
   cookie = authentication_agent_new_cookie (agent);
 
-  /* TODO: add uid 0 OR users in wheel group depending on value of @implicit_authorization */
   identities = NULL;
-  identities = g_list_prepend (identities, g_object_ref (user_of_subject));
-#if 0
-  identities = g_list_prepend (identities, polkit_unix_user_new (501));
-  identities = g_list_prepend (identities, polkit_unix_user_new (502));
-  identities = g_list_prepend (identities, polkit_unix_user_new (0));
-#endif
+
+  /* select admin user if required by the implicit authorization */
+  if (implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED ||
+      implicit_authorization == POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED_RETAINED)
+    {
+      identities = get_admin_auth_identities (authority);
+    }
+  else
+    {
+      identities = g_list_prepend (identities, g_object_ref (user_of_subject));
+    }
 
   session = authentication_session_new (agent,
                                         cookie,
@@ -2605,6 +2683,53 @@ check_temporary_authorization_for_identity (PolkitBackendLocalAuthority *authori
 }
 
 static GList *
+get_users_in_group (PolkitBackendLocalAuthority *authority,
+                    PolkitIdentity              *group,
+                    gboolean                     include_root)
+{
+  gid_t gid;
+  struct group *grp;
+  GList *ret;
+  guint n;
+
+  ret = NULL;
+
+  gid = polkit_unix_group_get_gid (POLKIT_UNIX_GROUP (group));
+  grp = getgrgid (gid);
+  if (grp == NULL)
+    {
+      g_warning ("Error looking up group with gid %d: %m", gid);
+      goto out;
+    }
+
+  for (n = 0; grp->gr_mem != NULL && grp->gr_mem[n] != NULL; n++)
+    {
+      PolkitIdentity *user;
+      GError *error;
+
+      if (!include_root && strcmp (grp->gr_mem[n], "root") == 0)
+        continue;
+
+      error = NULL;
+      user = polkit_unix_user_new_for_name (grp->gr_mem[n], &error);
+      if (user == NULL)
+        {
+          g_warning ("Unknown username '%s' in group: %s", grp->gr_mem[n], error->message);
+          g_error_free (error);
+        }
+      else
+        {
+          ret = g_list_prepend (ret, user);
+        }
+    }
+
+  ret = g_list_reverse (ret);
+
+ out:
+  return ret;
+}
+
+static GList *
 get_groups_for_user (PolkitBackendLocalAuthority *authority,
                      PolkitIdentity              *user)
 {
diff --git a/src/polkitbackend/polkitbackendtypes.h b/src/polkitbackend/polkitbackendtypes.h
index b2b0111..0e6d24a 100644
--- a/src/polkitbackend/polkitbackendtypes.h
+++ b/src/polkitbackend/polkitbackendtypes.h
@@ -33,6 +33,8 @@ typedef struct _PolkitBackendLocalAuthority PolkitBackendLocalAuthority;
 struct _PolkitBackendSessionMonitor;
 typedef struct _PolkitBackendSessionMonitor PolkitBackendSessionMonitor;
 
+struct _PolkitBackendConfigSource;
+typedef struct _PolkitBackendConfigSource PolkitBackendConfigSource;
 
 #endif /* __POLKIT_BACKEND_TYPES_H */
 


More information about the hal-commit mailing list