PolicyKit: Branch 'wip/js-rule-files'

David Zeuthen david at kemper.freedesktop.org
Thu May 17 20:29:00 PDT 2012


 configure.ac                                          |    4 
 src/polkitbackend/Makefile.am                         |    3 
 src/polkitbackend/polkitbackendauthority.c            |  109 ++
 src/polkitbackend/polkitbackendinteractiveauthority.c |  132 ++
 src/polkitbackend/polkitbackendjsauthority.c          |  831 ++++++++++++++++++
 src/polkitbackend/polkitbackendjsauthority.h          |   75 +
 src/polkitbackend/polkitbackendtypes.h                |    3 
 test/data/etc/polkit-1/rules.d/10-testing.rules       |   32 
 test/polkitbackend/Makefile.am                        |    4 
 test/polkitbackend/test-polkitbackendjsauthority.c    |  153 +++
 10 files changed, 1340 insertions(+), 6 deletions(-)

New commits:
commit aeb2b50a7b0ed1411df81790231cd902d6e76e56
Author: David Zeuthen <davidz at redhat.com>
Date:   Thu May 17 23:27:58 2012 -0400

    Add experimental authority backend using JavaScript rule files
    
    Signed-off-by: David Zeuthen <davidz at redhat.com>

diff --git a/configure.ac b/configure.ac
index f325922..4f2ac6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -127,6 +127,10 @@ PKG_CHECK_MODULES(GLIB, [gio-2.0 >= 2.28.0])
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
+PKG_CHECK_MODULES(LIBJS, [libjs >= 1.8.5])
+AC_SUBST(LIBJS_CFLAGS)
+AC_SUBST(LIBJS_LIBS)
+
 EXPAT_LIB=""
 AC_ARG_WITH(expat, [  --with-expat=<dir>      Use expat from here],
                       [
diff --git a/src/polkitbackend/Makefile.am b/src/polkitbackend/Makefile.am
index b91cafa..2f6fd05 100644
--- a/src/polkitbackend/Makefile.am
+++ b/src/polkitbackend/Makefile.am
@@ -37,6 +37,7 @@ libpolkit_backend_1_la_SOURCES =                                   			\
 	polkitbackendauthority.h		polkitbackendauthority.c		\
 	polkitbackendinteractiveauthority.h	polkitbackendinteractiveauthority.c	\
 	polkitbackendlocalauthority.h		polkitbackendlocalauthority.c		\
+	polkitbackendjsauthority.h		polkitbackendjsauthority.c		\
 	polkitbackendactionpool.h		polkitbackendactionpool.c		\
 	polkitbackendconfigsource.h		polkitbackendconfigsource.c		\
 	polkitbackendactionlookup.h		polkitbackendactionlookup.c		\
@@ -56,6 +57,7 @@ libpolkit_backend_1_la_CFLAGS =                                        	\
         -D_POLKIT_BACKEND_COMPILATION                                  	\
         $(GLIB_CFLAGS)							\
 	$(SYSTEMD_CFLAGS)						\
+	$(LIBJS_CFLAGS)							\
         $(NULL)
 
 libpolkit_backend_1_la_LIBADD =                               		\
@@ -63,6 +65,7 @@ libpolkit_backend_1_la_LIBADD =                               		\
 	$(SYSTEMD_LIBS)							\
 	$(top_builddir)/src/polkit/libpolkit-gobject-1.la		\
 	$(EXPAT_LIBS)							\
+	$(LIBJS_LIBS)							\
         $(NULL)
 
 libpolkit_backend_1_la_LDFLAGS = -export-symbols-regex '(^polkit_.*)'
diff --git a/src/polkitbackend/polkitbackendauthority.c b/src/polkitbackend/polkitbackendauthority.c
index fd4f161..e127247 100644
--- a/src/polkitbackend/polkitbackendauthority.c
+++ b/src/polkitbackend/polkitbackendauthority.c
@@ -31,6 +31,7 @@
 
 #include "polkitbackendauthority.h"
 #include "polkitbackendlocalauthority.h"
+#include "polkitbackendjsauthority.h"
 
 #include "polkitbackendprivate.h"
 
@@ -1359,6 +1360,7 @@ polkit_backend_authority_get (void)
 {
   static GIOExtensionPoint *ep = NULL;
   static volatile GType local_authority_type = G_TYPE_INVALID;
+  static volatile GType js_authority_type = G_TYPE_INVALID;
   GList *modules;
   GList *authority_implementations;
   GType authority_type;
@@ -1374,9 +1376,9 @@ polkit_backend_authority_get (void)
 
   /* make sure local types are registered */
   if (local_authority_type == G_TYPE_INVALID)
-    {
-      local_authority_type = POLKIT_BACKEND_TYPE_LOCAL_AUTHORITY;
-    }
+    local_authority_type = POLKIT_BACKEND_TYPE_LOCAL_AUTHORITY;
+  if (js_authority_type == G_TYPE_INVALID)
+    js_authority_type = POLKIT_BACKEND_TYPE_JS_AUTHORITY;
 
   /* load all modules */
   modules = g_io_modules_load_all_in_directory (PACKAGE_LIB_DIR "/polkit-1/extensions");
@@ -1416,17 +1418,114 @@ polkit_backend_authority_get (void)
   return authority;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef enum
+{
+  _COLOR_RESET,
+  _COLOR_BOLD_ON,
+  _COLOR_INVERSE_ON,
+  _COLOR_BOLD_OFF,
+  _COLOR_FG_BLACK,
+  _COLOR_FG_RED,
+  _COLOR_FG_GREEN,
+  _COLOR_FG_YELLOW,
+  _COLOR_FG_BLUE,
+  _COLOR_FG_MAGENTA,
+  _COLOR_FG_CYAN,
+  _COLOR_FG_WHITE,
+  _COLOR_BG_RED,
+  _COLOR_BG_GREEN,
+  _COLOR_BG_YELLOW,
+  _COLOR_BG_BLUE,
+  _COLOR_BG_MAGENTA,
+  _COLOR_BG_CYAN,
+  _COLOR_BG_WHITE
+} _Color;
+
+static gboolean _color_stdin_is_tty = FALSE;
+static gboolean _color_initialized = FALSE;
+
+static void
+_color_init (void)
+{
+  if (_color_initialized)
+    return;
+  _color_initialized = TRUE;
+  _color_stdin_is_tty = (isatty (STDIN_FILENO) != 0 && isatty (STDOUT_FILENO) != 0);
+}
+
+static const gchar *
+_color_get (_Color color)
+{
+  const gchar *str;
+
+  _color_init ();
+
+  if (!_color_stdin_is_tty)
+    return "";
+
+  str = NULL;
+  switch (color)
+    {
+    case _COLOR_RESET:      str="\x1b[0m"; break;
+    case _COLOR_BOLD_ON:    str="\x1b[1m"; break;
+    case _COLOR_INVERSE_ON: str="\x1b[7m"; break;
+    case _COLOR_BOLD_OFF:   str="\x1b[22m"; break;
+    case _COLOR_FG_BLACK:   str="\x1b[30m"; break;
+    case _COLOR_FG_RED:     str="\x1b[31m"; break;
+    case _COLOR_FG_GREEN:   str="\x1b[32m"; break;
+    case _COLOR_FG_YELLOW:  str="\x1b[33m"; break;
+    case _COLOR_FG_BLUE:    str="\x1b[34m"; break;
+    case _COLOR_FG_MAGENTA: str="\x1b[35m"; break;
+    case _COLOR_FG_CYAN:    str="\x1b[36m"; break;
+    case _COLOR_FG_WHITE:   str="\x1b[37m"; break;
+    case _COLOR_BG_RED:     str="\x1b[41m"; break;
+    case _COLOR_BG_GREEN:   str="\x1b[42m"; break;
+    case _COLOR_BG_YELLOW:  str="\x1b[43m"; break;
+    case _COLOR_BG_BLUE:    str="\x1b[44m"; break;
+    case _COLOR_BG_MAGENTA: str="\x1b[45m"; break;
+    case _COLOR_BG_CYAN:    str="\x1b[46m"; break;
+    case _COLOR_BG_WHITE:   str="\x1b[47m"; break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+  return str;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 void
 polkit_backend_authority_log (PolkitBackendAuthority *authority,
                               const gchar *format,
                               ...)
 {
+  GTimeVal now;
+  time_t now_time;
+  struct tm *now_tm;
+  gchar time_buf[128];
+  gchar *message;
   va_list var_args;
 
   g_return_if_fail (POLKIT_BACKEND_IS_AUTHORITY (authority));
 
   va_start (var_args, format);
-  vsyslog (LOG_NOTICE, format, var_args);
-
+  message = g_strdup_vprintf (format, var_args);
   va_end (var_args);
+
+  va_start (var_args, format);
+  syslog (LOG_NOTICE, "%s", message);
+
+  g_get_current_time (&now);
+  now_time = (time_t) now.tv_sec;
+  now_tm = localtime (&now_time);
+  strftime (time_buf, sizeof time_buf, "%H:%M:%S", now_tm);
+  g_print ("%s%s%s.%03d%s: %s\n",
+           _color_get (_COLOR_BOLD_ON), _color_get (_COLOR_FG_YELLOW),
+           time_buf, (gint) now.tv_usec / 1000,
+           _color_get (_COLOR_RESET),
+           message);
+
+  g_free (message);
 }
diff --git a/src/polkitbackend/polkitbackendinteractiveauthority.c b/src/polkitbackend/polkitbackendinteractiveauthority.c
index b237e9d..5f6eea5 100644
--- a/src/polkitbackend/polkitbackendinteractiveauthority.c
+++ b/src/polkitbackend/polkitbackendinteractiveauthority.c
@@ -23,6 +23,7 @@
 #include <errno.h>
 #include <pwd.h>
 #include <grp.h>
+#include <netdb.h>
 #include <string.h>
 #include <glib/gstdio.h>
 #include <locale.h>
@@ -2059,6 +2060,110 @@ add_pid (PolkitDetails *details,
   ;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GList *
+get_users_in_group (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: %s", gid, g_strerror (errno));
+      goto out;
+    }
+
+  for (n = 0; grp->gr_mem != NULL && grp->gr_mem[n] != NULL; n++)
+    {
+      PolkitIdentity *user;
+      GError *error;
+
+      if (!include_root && g_strcmp0 (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_users_in_net_group (PolkitIdentity                    *group,
+                        gboolean                           include_root)
+{
+  const gchar *name;
+  GList *ret;
+
+  ret = NULL;
+  name = polkit_unix_netgroup_get_name (POLKIT_UNIX_NETGROUP (group));
+
+  if (setnetgrent (name) == 0)
+    {
+      g_warning ("Error looking up net group with name %s: %s", name, g_strerror (errno));
+      goto out;
+    }
+
+  for (;;)
+    {
+      char *hostname, *username, *domainname;
+      PolkitIdentity *user;
+      GError *error = NULL;
+
+      if (getnetgrent (&hostname, &username, &domainname) == 0)
+        break;
+
+      /* Skip NULL entries since we never want to make everyone an admin
+       * Skip "-" entries which mean "no match ever" in netgroup land */
+      if (username == NULL || g_strcmp0 (username, "-") == 0)
+        continue;
+
+      /* TODO: Should we match on hostname? Maybe only allow "-" as a hostname
+       * for safety. */
+
+      user = polkit_unix_user_new_for_name (username, &error);
+      if (user == NULL)
+        {
+          g_warning ("Unknown username '%s' in unix-netgroup: %s", username, error->message);
+          g_error_free (error);
+        }
+      else
+        {
+          ret = g_list_prepend (ret, user);
+        }
+    }
+
+  ret = g_list_reverse (ret);
+
+ out:
+  endnetgrent ();
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 authentication_agent_initiate_challenge (AuthenticationAgent         *agent,
                                          PolkitSubject               *subject,
@@ -2080,6 +2185,7 @@ authentication_agent_initiate_challenge (AuthenticationAgent         *agent,
   gchar *localized_icon_name;
   PolkitDetails *localized_details;
   GVariant *details_gvariant;
+  GList *user_identities = NULL;
   GVariantBuilder identities_builder;
   GVariant *parameters;
 
@@ -2138,10 +2244,33 @@ authentication_agent_initiate_challenge (AuthenticationAgent         *agent,
   details_gvariant = polkit_details_to_gvariant (localized_details);
   g_variant_ref_sink (details_gvariant);
 
-  g_variant_builder_init (&identities_builder, G_VARIANT_TYPE ("a(sa{sv})"));
+  /* expand groups/netgroups to users */
+  user_identities = NULL;
   for (l = identities; l != NULL; l = l->next)
     {
       PolkitIdentity *identity = POLKIT_IDENTITY (l->data);
+      if (POLKIT_IS_UNIX_USER (identity))
+        {
+          user_identities = g_list_append (user_identities, g_object_ref (identity));
+        }
+      else if (POLKIT_IS_UNIX_GROUP (identity))
+        {
+          user_identities = g_list_concat (user_identities, get_users_in_group (identity, FALSE));
+        }
+      else if (POLKIT_IS_UNIX_NETGROUP (identity))
+        {
+          user_identities =  g_list_concat (user_identities, get_users_in_net_group (identity, FALSE));
+        }
+      else
+        {
+          g_warning ("Unsupported identity");
+        }
+    }
+
+  g_variant_builder_init (&identities_builder, G_VARIANT_TYPE ("a(sa{sv})"));
+  for (l = user_identities; l != NULL; l = l->next)
+    {
+      PolkitIdentity *identity = POLKIT_IDENTITY (l->data);
       GVariant *value;
       value = polkit_identity_to_gvariant (identity);
       g_variant_ref_sink (value);
@@ -2167,6 +2296,7 @@ authentication_agent_initiate_challenge (AuthenticationAgent         *agent,
                      (GAsyncReadyCallback) authentication_agent_begin_cb,
                      session);
 
+  g_list_free_full (user_identities, g_object_unref);
   g_list_foreach (identities, (GFunc) g_object_unref, NULL);
   g_list_free (identities);
   g_free (cookie);
diff --git a/src/polkitbackend/polkitbackendjsauthority.c b/src/polkitbackend/polkitbackendjsauthority.c
new file mode 100644
index 0000000..23d2640
--- /dev/null
+++ b/src/polkitbackend/polkitbackendjsauthority.c
@@ -0,0 +1,831 @@
+/*
+ * Copyright (C) 2008-2012 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 <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <string.h>
+#include <glib/gstdio.h>
+#include <locale.h>
+#include <glib/gi18n-lib.h>
+
+#include <polkit/polkit.h>
+#include "polkitbackendjsauthority.h"
+
+#include <polkit/polkitprivate.h>
+
+#include <jsapi.h>
+
+/**
+ * SECTION:polkitbackendjsauthority
+ * @title: PolkitBackendJsAuthority
+ * @short_description: JS Authority
+ * @stability: Unstable
+ *
+ * An implementation of #PolkitBackendAuthority that reads and
+ * evalates Javascript files and supports interaction with
+ * authentication agents (virtue of being based on
+ * #PolkitBackendInteractiveAuthority).
+ */
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+struct _PolkitBackendJsAuthorityPrivate
+{
+  gchar *rules_dir;
+  GFileMonitor *dir_monitor;
+
+  JSRuntime *rt;
+  JSContext *cx;
+  JSObject *js_global;
+  JSObject *js_polkit;
+
+  /* A list of JSObject instances */
+  GList *scripts;
+};
+
+static void on_dir_monitor_changed (GFileMonitor     *monitor,
+                                    GFile            *file,
+                                    GFile            *other_file,
+                                    GFileMonitorEvent event_type,
+                                    gpointer          user_data);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+enum
+{
+  PROP_0,
+  PROP_RULES_DIR,
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GList *polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *authority,
+                                                                     PolkitSubject                     *caller,
+                                                                     PolkitSubject                     *subject,
+                                                                     PolkitIdentity                    *user_for_subject,
+                                                                     const gchar                       *action_id,
+                                                                     PolkitDetails                     *details);
+
+static PolkitImplicitAuthorization polkit_backend_js_authority_check_authorization_sync (
+                                                          PolkitBackendInteractiveAuthority *authority,
+                                                          PolkitSubject                     *caller,
+                                                          PolkitSubject                     *subject,
+                                                          PolkitIdentity                    *user_for_subject,
+                                                          gboolean                           subject_is_local,
+                                                          gboolean                           subject_is_active,
+                                                          const gchar                       *action_id,
+                                                          PolkitDetails                     *details,
+                                                          PolkitImplicitAuthorization        implicit,
+                                                          PolkitDetails                     *out_details);
+
+G_DEFINE_TYPE_WITH_CODE (PolkitBackendJsAuthority,
+                         polkit_backend_js_authority,
+                         POLKIT_BACKEND_TYPE_INTERACTIVE_AUTHORITY,
+                         g_io_extension_point_implement (POLKIT_BACKEND_AUTHORITY_EXTENSION_POINT_NAME,
+                                                         g_define_type_id,
+                                                         "js-authority" PACKAGE_VERSION,
+                                                         10));
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static JSClass js_global_class = {
+  "global",
+  JSCLASS_GLOBAL_FLAGS,
+  JS_PropertyStub,
+  JS_PropertyStub,
+  JS_PropertyStub,
+  JS_StrictPropertyStub,
+  JS_EnumerateStub,
+  JS_ResolveStub,
+  JS_ConvertStub,
+  JS_FinalizeStub,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static JSClass js_polkit_class = {
+  "Polkit",
+  0,
+  JS_PropertyStub,
+  JS_PropertyStub,
+  JS_PropertyStub,
+  JS_StrictPropertyStub,
+  JS_EnumerateStub,
+  JS_ResolveStub,
+  JS_ConvertStub,
+  JS_FinalizeStub,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+static JSBool js_polkit_log (JSContext *cx, uintN argc, jsval *vp);
+
+static JSFunctionSpec js_polkit_functions[] =
+{
+  JS_FS("log",            js_polkit_log,            0, 0),
+  JS_FS_END
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void report_error (JSContext     *cx,
+                          const char    *message,
+                          JSErrorReport *report)
+{
+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (JS_GetContextPrivate (cx));
+  polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+                                "%s:%u: %s",
+                                report->filename ? report->filename : "<no filename>",
+                                (unsigned int) report->lineno,
+                                message);
+}
+
+static void
+polkit_backend_js_authority_init (PolkitBackendJsAuthority *authority)
+{
+  authority->priv = G_TYPE_INSTANCE_GET_PRIVATE (authority,
+                                                 POLKIT_BACKEND_TYPE_JS_AUTHORITY,
+                                                 PolkitBackendJsAuthorityPrivate);
+}
+
+static void
+load_scripts (PolkitBackendJsAuthority  *authority)
+{
+  GDir *dir = NULL;
+  GList *files = NULL;
+  GList *l;
+  const gchar *name;
+  guint num_scripts = 0;
+  GError *error = NULL;
+
+  polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+                                "Loading scripts from directory %s",
+                                authority->priv->rules_dir);
+
+  dir = g_dir_open (authority->priv->rules_dir,
+                    0,
+                    &error);
+  if (dir == NULL)
+    {
+      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+                                    "Error opening rules directory: %s (%s, %d)\n",
+                                    error->message, g_quark_to_string (error->domain), error->code);
+      g_clear_error (&error);
+      goto out;
+    }
+
+  files = NULL;
+  while ((name = g_dir_read_name (dir)) != NULL)
+    {
+      if (g_str_has_suffix (name, ".rules"))
+        files = g_list_prepend (files, g_strdup_printf ("%s/%s", authority->priv->rules_dir, name));
+    }
+
+  files = g_list_sort (files, (GCompareFunc) g_strcmp0);
+
+  for (l = files; l != NULL; l = l->next)
+    {
+      const gchar *filename = l->data;
+      JSObject *script;
+
+      script = JS_CompileFile (authority->priv->cx,
+                               authority->priv->js_global,
+                               filename);
+      if (script == NULL)
+        {
+          polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+                                        "Error compiling script %s",
+                                        filename);
+          continue;
+        }
+
+      /* evaluate the script */
+      jsval rval;
+      if (!JS_ExecuteScript (authority->priv->cx,
+                             authority->priv->js_global,
+                             script,
+                             &rval))
+        {
+          polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+                                        "Error executing script %s",
+                                        filename);
+          continue;
+        }
+
+      //g_print ("Successfully loaded and evaluated script `%s'\n", filename);
+
+      num_scripts++;
+    }
+
+  polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+                                "Finished loading, compiling and executing %d scripts",
+                                num_scripts);
+
+ out:
+  g_list_free_full (files, g_free);
+  if (dir != NULL)
+    g_dir_close (dir);
+}
+
+static void
+reload_scripts (PolkitBackendJsAuthority *authority)
+{
+  jsval argv[1] = {0};
+  jsval rval = {0};
+
+  if (!JS_CallFunctionName(authority->priv->cx,
+                           authority->priv->js_polkit,
+                           "_deleteRules",
+                           0,
+                           argv,
+                           &rval))
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, faileded clearing rules\n");
+      goto out;
+    }
+
+  load_scripts (authority);
+ out:
+  ;
+}
+
+static void
+on_dir_monitor_changed (GFileMonitor     *monitor,
+                        GFile            *file,
+                        GFile            *other_file,
+                        GFileMonitorEvent event_type,
+                        gpointer          user_data)
+{
+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (user_data);
+
+  /* TODO: maybe rate-limit so storms of events are collapsed into one with a 500ms resolution?
+   *       Because when editing a file with emacs we get 4-8 events..
+   */
+
+  if (file != NULL)
+    {
+      gchar *name;
+
+      name = g_file_get_basename (file);
+
+      /* g_print ("event_type=%d file=%p name=%s\n", event_type, file, name); */
+      if (!g_str_has_prefix (name, ".") &&
+          !g_str_has_prefix (name, "#") &&
+          g_str_has_suffix (name, ".rules") &&
+          (event_type == G_FILE_MONITOR_EVENT_CREATED ||
+           event_type == G_FILE_MONITOR_EVENT_DELETED ||
+           event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT))
+        {
+          polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
+                                        "Reloading scripts");
+          reload_scripts (authority);
+        }
+      g_free (name);
+    }
+}
+
+static const gchar js_polkit_init[] =
+  "polkit._administratorRuleFuncs = [];\n"
+  "polkit.addAdministratorRule = function(callback) {this._administratorRuleFuncs.push(callback);};\n"
+  "polkit._runAdministratorRules = function(action, pid, user, groups, is_local, is_active) {\n"
+  "  var ret = null;\n"
+  "  var subject = {};\n"
+  "  subject.pid = pid;\n"
+  "  subject.user = user;\n"
+  "  subject.local = is_local;\n"
+  "  subject.active = is_active;\n"
+  "  subject.groups = groups.split(',');\n"
+  "  for (var n = this._administratorRuleFuncs.length - 1; n >= 0; n--) {\n"
+  "    var func = this._administratorRuleFuncs[n];\n"
+  "    ret = func(action, subject);\n"
+  "    if (ret)\n"
+  "      break\n"
+  "  }\n"
+  "  return ret.join(',');\n"
+  "};\n"
+  "\n"
+  "polkit._authorizationRuleFuncs = [];\n"
+  "polkit.addAuthorizationRule = function(callback) {this._authorizationRuleFuncs.push(callback);};\n"
+  "polkit._runAuthorizationRules = function(action, pid, user, groups, is_local, is_active) {\n"
+  "  var ret = null;\n"
+  "  var subject = {};\n"
+  "  subject.pid = pid;\n"
+  "  subject.user = user;\n"
+  "  subject.local = is_local;\n"
+  "  subject.active = is_active;\n"
+  "  subject.groups = groups.split(',');\n"
+  "  for (var n = this._authorizationRuleFuncs.length - 1; n >= 0; n--) {\n"
+  "    var func = this._authorizationRuleFuncs[n];\n"
+  "    ret = func(action, subject);\n"
+  "    if (ret)\n"
+  "      break\n"
+  "  }\n"
+  "  return ret;\n"
+  "};\n"
+  "\n"
+  "polkit._deleteRules = function() {\n"
+  "  this._administratorRuleFuncs = [];\n"
+  "  this._authorizationRuleFuncs = [];\n"
+  "};\n"
+  "\n"
+  "";
+
+
+static void
+polkit_backend_js_authority_constructed (GObject *object)
+{
+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+
+  /* TODO: error checking */
+  authority->priv->rt = JS_NewRuntime (8L * 1024L * 1024L);
+  authority->priv->cx = JS_NewContext (authority->priv->rt, 8192);
+  JS_SetOptions (authority->priv->cx,
+                 JSOPTION_VAROBJFIX |
+                 JSOPTION_JIT |
+                 JSOPTION_METHODJIT);
+  JS_SetVersion(authority->priv->cx, JSVERSION_LATEST);
+  JS_SetErrorReporter(authority->priv->cx, report_error);
+  JS_SetContextPrivate (authority->priv->cx, authority);
+
+  authority->priv->js_global = JS_NewCompartmentAndGlobalObject (authority->priv->cx,
+                                                                 &js_global_class,
+                                                                 NULL);
+  JS_InitStandardClasses (authority->priv->cx, authority->priv->js_global);
+
+  authority->priv->js_polkit = JS_DefineObject(authority->priv->cx,
+                                               authority->priv->js_global,
+                                               "polkit",
+                                               &js_polkit_class,
+                                               NULL,
+                                               JSPROP_ENUMERATE);
+  JS_DefineFunctions (authority->priv->cx,
+                      authority->priv->js_polkit,
+                      js_polkit_functions);
+
+  if (!JS_EvaluateScript (authority->priv->cx,
+                          authority->priv->js_global,
+                          js_polkit_init,
+                          strlen (js_polkit_init),
+                          NULL,  /* filename */
+                          0,     /* lineno */
+                          NULL)) /* rval */
+    {
+      g_printerr ("Error running init code\n");
+    }
+
+  load_scripts (authority);
+
+  G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->constructed (object);
+}
+
+static void
+polkit_backend_js_authority_finalize (GObject *object)
+{
+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+
+  g_free (authority->priv->rules_dir);
+  if (authority->priv->dir_monitor != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (authority->priv->dir_monitor,
+                                            G_CALLBACK (on_dir_monitor_changed),
+                                            authority);
+      g_object_unref (authority->priv->dir_monitor);
+    }
+
+  JS_DestroyContext (authority->priv->cx);
+  JS_DestroyRuntime (authority->priv->rt);
+  /* JS_ShutDown (); */
+
+  G_OBJECT_CLASS (polkit_backend_js_authority_parent_class)->finalize (object);
+}
+
+static void
+polkit_backend_js_authority_set_property (GObject      *object,
+                                          guint         property_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (object);
+  GFile *file;
+  GError *error;
+
+  switch (property_id)
+    {
+      case PROP_RULES_DIR:
+        g_assert (authority->priv->rules_dir == NULL);
+        authority->priv->rules_dir = g_value_dup_string (value);
+
+        file = g_file_new_for_path (authority->priv->rules_dir);
+        error = NULL;
+        authority->priv->dir_monitor = g_file_monitor_directory (file,
+                                                                 G_FILE_MONITOR_NONE,
+                                                                 NULL,
+                                                                 &error);
+        if (authority->priv->dir_monitor == NULL)
+          {
+            g_warning ("Error monitoring directory %s: %s",
+                       authority->priv->rules_dir,
+                       error->message);
+            g_clear_error (&error);
+          }
+        else
+          {
+            g_signal_connect (authority->priv->dir_monitor,
+                              "changed",
+                              G_CALLBACK (on_dir_monitor_changed),
+                              authority);
+          }
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static const gchar *
+polkit_backend_js_authority_get_name (PolkitBackendAuthority *authority)
+{
+  return "js";
+}
+
+static const gchar *
+polkit_backend_js_authority_get_version (PolkitBackendAuthority *authority)
+{
+  return PACKAGE_VERSION;
+}
+
+static PolkitAuthorityFeatures
+polkit_backend_js_authority_get_features (PolkitBackendAuthority *authority)
+{
+  return POLKIT_AUTHORITY_FEATURES_TEMPORARY_AUTHORIZATION;
+}
+
+static void
+polkit_backend_js_authority_class_init (PolkitBackendJsAuthorityClass *klass)
+{
+  GObjectClass *gobject_class;
+  PolkitBackendAuthorityClass *authority_class;
+  PolkitBackendInteractiveAuthorityClass *interactive_authority_class;
+
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize                               = polkit_backend_js_authority_finalize;
+  gobject_class->set_property                           = polkit_backend_js_authority_set_property;
+  gobject_class->constructed                            = polkit_backend_js_authority_constructed;
+
+  authority_class = POLKIT_BACKEND_AUTHORITY_CLASS (klass);
+  authority_class->get_name                             = polkit_backend_js_authority_get_name;
+  authority_class->get_version                          = polkit_backend_js_authority_get_version;
+  authority_class->get_features                         = polkit_backend_js_authority_get_features;
+
+  interactive_authority_class = POLKIT_BACKEND_INTERACTIVE_AUTHORITY_CLASS (klass);
+  interactive_authority_class->get_admin_identities     = polkit_backend_js_authority_get_admin_auth_identities;
+  interactive_authority_class->check_authorization_sync = polkit_backend_js_authority_check_authorization_sync;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_RULES_DIR,
+                                   g_param_spec_string ("rules-dir",
+                                                        NULL,
+                                                        NULL,
+                                                        PACKAGE_SYSCONF_DIR "/polkit-1/rules.d",
+                                                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+
+
+  g_type_class_add_private (klass, sizeof (PolkitBackendJsAuthorityPrivate));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+subject_to_js (PolkitBackendJsAuthority *authority,
+               PolkitSubject            *subject,
+               PolkitIdentity           *user_for_subject,
+               jsval                    *jsval_pid,
+               jsval                    *jsval_user,
+               jsval                    *jsval_groups,
+               GError                  **error)
+{
+  gboolean ret = FALSE;
+  JSString *user_name_jstr;
+  JSString *groups_jstr;
+  pid_t pid;
+  uid_t uid;
+  gchar *user_name = NULL;
+  GString *groups = NULL;
+  struct passwd *passwd;
+
+  g_return_val_if_fail (jsval_pid != NULL, FALSE);
+  g_return_val_if_fail (jsval_user != NULL, FALSE);
+  g_return_val_if_fail (jsval_groups != NULL, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (POLKIT_IS_UNIX_PROCESS (subject))
+    {
+      pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject));
+    }
+  else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
+    {
+      PolkitSubject *process;
+      process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject), NULL, error);
+      if (process == NULL)
+        goto out;
+      pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process));
+      g_object_unref (process);
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+
+  g_assert (POLKIT_IS_UNIX_USER (user_for_subject));
+  uid = polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_for_subject));
+
+  groups = g_string_new (NULL);
+
+  passwd = getpwuid (uid);
+  if (passwd == NULL)
+    {
+      user_name = g_strdup_printf ("%d", (gint) uid);
+      g_warning ("Error looking up info for uid %d: %m", (gint) uid);
+    }
+  else
+    {
+      gid_t gids[512];
+      int num_gids = 512;
+
+      user_name = g_strdup (passwd->pw_name);
+
+      if (getgrouplist (passwd->pw_name,
+                        passwd->pw_gid,
+                        gids,
+                        &num_gids) < 0)
+        {
+          g_warning ("Error looking up groups for uid %d: %m", (gint) uid);
+        }
+      else
+        {
+          gint n;
+          for (n = 0; n < num_gids; n++)
+            {
+              struct group *group;
+              if (n > 0)
+                g_string_append_c (groups, ',');
+
+              group = getgrgid (gids[n]);
+              if (group == NULL)
+                {
+                  g_string_append_printf (groups, "%d", (gint) gids[n]);
+                }
+              else
+                {
+                  g_string_append_printf (groups, "%s", group->gr_name);
+                }
+            }
+        }
+    }
+
+  user_name_jstr = JS_NewStringCopyZ (authority->priv->cx, user_name);
+  groups_jstr = JS_NewStringCopyZ (authority->priv->cx, groups->str);
+  *jsval_pid = INT_TO_JSVAL ((int32) pid);
+  *jsval_user = STRING_TO_JSVAL (user_name_jstr);
+  *jsval_groups = STRING_TO_JSVAL (groups_jstr);
+
+  ret = TRUE;
+
+ out:
+  /* TODO: are we leaking _jstr ? */
+  g_free (user_name);
+  if (groups != NULL)
+    g_string_free (groups, TRUE);
+  return ret;
+}
+
+
+static GList *
+polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveAuthority *_authority,
+                                                       PolkitSubject                     *caller,
+                                                       PolkitSubject                     *subject,
+                                                       PolkitIdentity                    *user_for_subject,
+                                                       const gchar                       *action_id,
+                                                       PolkitDetails                     *details)
+{
+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
+  GList *ret = NULL;
+  jsval argv[6] = {0};
+  jsval rval = {0};
+  JSString *action_id_jstr;
+  guint n;
+  GError *error = NULL;
+  JSString *ret_jsstr;
+  gchar *ret_str = NULL;
+  gchar **ret_strs = NULL;
+
+  if (!subject_to_js (authority, subject, user_for_subject, &argv[1], &argv[2], &argv[3], &error))
+    {
+      /* TODO: syslog? */
+      g_printerr ("Error converting subject: %s\n", error->message);
+      g_clear_error (&error);
+      goto out;
+    }
+
+  action_id_jstr = JS_NewStringCopyZ (authority->priv->cx, action_id);
+  argv[0] = STRING_TO_JSVAL (action_id_jstr);
+  argv[4] = BOOLEAN_TO_JSVAL (FALSE);//TODO:subject_is_local);
+  argv[5] = BOOLEAN_TO_JSVAL (FALSE);//TODO:subject_is_active);
+
+  if (!JS_CallFunctionName(authority->priv->cx,
+                           authority->priv->js_polkit,
+                           "_runAdministratorRules",
+                           6,
+                           argv,
+                           &rval))
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, failed\n");
+      goto out;
+    }
+
+  if (!JSVAL_IS_STRING (rval) && !JSVAL_IS_NULL (rval))
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, not string\n");
+      goto out;
+    }
+
+  ret_jsstr = JSVAL_TO_STRING (rval);
+  ret_str = g_utf16_to_utf8 (JS_GetStringCharsZ (authority->priv->cx, ret_jsstr), -1, NULL, NULL, NULL);
+  if (ret_str == NULL)
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, error converting to UTF-8\n");
+      goto out;
+    }
+
+  //g_print ("yay, worked `%s'\n", ret_str);
+
+  ret_strs = g_strsplit (ret_str, ",", -1);
+  for (n = 0; ret_strs != NULL && ret_strs[n] != NULL; n++)
+    {
+      const gchar *identity_str = ret_strs[n];
+      PolkitIdentity *identity;
+
+      error = NULL;
+      identity = polkit_identity_from_string (identity_str, &error);
+      if (identity == NULL)
+        {
+          /* TODO: syslog? */
+          g_printerr ("boo, identity `%s' is not valid, ignoring\n", identity_str);
+        }
+      else
+        {
+          ret = g_list_prepend (ret, identity);
+        }
+    }
+  ret = g_list_reverse (ret);
+
+ out:
+  g_strfreev (ret_strs);
+  g_free (ret_str);
+  /* fallback to root password auth */
+  if (ret == NULL)
+    ret = g_list_prepend (ret, polkit_unix_user_new (0));
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static PolkitImplicitAuthorization
+polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAuthority *_authority,
+                                                      PolkitSubject                     *caller,
+                                                      PolkitSubject                     *subject,
+                                                      PolkitIdentity                    *user_for_subject,
+                                                      gboolean                           subject_is_local,
+                                                      gboolean                           subject_is_active,
+                                                      const gchar                       *action_id,
+                                                      PolkitDetails                     *details,
+                                                      PolkitImplicitAuthorization        implicit,
+                                                      PolkitDetails                     *out_details)
+{
+  PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
+  PolkitImplicitAuthorization ret = implicit;
+  jsval argv[6] = {0};
+  jsval rval = {0};
+  JSString *action_id_jstr;
+  GError *error = NULL;
+  JSString *ret_jsstr;
+  const jschar *ret_utf16;
+  gchar *ret_str = NULL;
+
+  if (!subject_to_js (authority, subject, user_for_subject, &argv[1], &argv[2], &argv[3], &error))
+    {
+      /* TODO: syslog? */
+      g_printerr ("Error converting subject: %s\n", error->message);
+      g_clear_error (&error);
+      goto out;
+    }
+
+  action_id_jstr = JS_NewStringCopyZ (authority->priv->cx, action_id);
+  argv[0] = STRING_TO_JSVAL (action_id_jstr);
+  argv[4] = BOOLEAN_TO_JSVAL (subject_is_local);
+  argv[5] = BOOLEAN_TO_JSVAL (subject_is_active);
+
+  if (!JS_CallFunctionName(authority->priv->cx,
+                           authority->priv->js_polkit,
+                           "_runAuthorizationRules",
+                           6,
+                           argv,
+                           &rval))
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, failed\n");
+      goto out;
+    }
+
+  if (!JSVAL_IS_STRING (rval) && !JSVAL_IS_NULL (rval))
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, not string\n");
+      goto out;
+    }
+
+  ret_jsstr = JSVAL_TO_STRING (rval);
+  if (ret_jsstr == NULL)
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, string is null\n");
+      goto out;
+    }
+
+  ret_utf16 = JS_GetStringCharsZ (authority->priv->cx, ret_jsstr);
+  ret_str = g_utf16_to_utf8 (ret_utf16, -1, NULL, NULL, NULL);
+  if (ret_str == NULL)
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, error converting to UTF-8\n");
+      goto out;
+    }
+
+  if (!polkit_implicit_authorization_from_string (ret_str, &ret))
+    {
+      /* TODO: syslog? */
+      g_printerr ("boo, returned result `%s' is not valid\n", ret_str);
+      goto out;
+    }
+
+  g_print ("yay, worked `%s'\n", ret_str);
+
+ out:
+  g_free (ret_str);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static JSBool
+js_polkit_log (JSContext  *cx,
+               uintN       argc,
+               jsval      *vp)
+{
+  /* PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (JS_GetContextPrivate (cx)); */
+  JSBool ret = JS_FALSE;
+  JSString *str;
+  char *s;
+
+  if (!JS_ConvertArguments (cx, argc, JS_ARGV (cx, vp), "S", &str))
+    goto out;
+
+  s = JS_EncodeString (cx, str);
+  JS_ReportWarning (cx, s);
+  JS_free (cx, s);
+
+  ret = JS_TRUE;
+
+  JS_SET_RVAL (cx, vp, JSVAL_VOID);  /* return undefined */
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/polkitbackend/polkitbackendjsauthority.h b/src/polkitbackend/polkitbackendjsauthority.h
new file mode 100644
index 0000000..6fd283b
--- /dev/null
+++ b/src/polkitbackend/polkitbackendjsauthority.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008-2012 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_JS_AUTHORITY_H
+#define __POLKIT_BACKEND_JS_AUTHORITY_H
+
+#include <glib-object.h>
+#include <polkitbackend/polkitbackendtypes.h>
+#include <polkitbackend/polkitbackendinteractiveauthority.h>
+
+G_BEGIN_DECLS
+
+#define POLKIT_BACKEND_TYPE_JS_AUTHORITY         (polkit_backend_js_authority_get_type ())
+#define POLKIT_BACKEND_JS_AUTHORITY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), POLKIT_BACKEND_TYPE_JS_AUTHORITY, PolkitBackendJsAuthority))
+#define POLKIT_BACKEND_JS_AUTHORITY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), POLKIT_BACKEND_TYPE_JS_AUTHORITY, PolkitBackendJsAuthorityClass))
+#define POLKIT_BACKEND_JS_AUTHORITY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), POLKIT_BACKEND_TYPE_JS_AUTHORITY,PolkitBackendJsAuthorityClass))
+#define POLKIT_BACKEND_IS_JS_AUTHORITY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), POLKIT_BACKEND_TYPE_JS_AUTHORITY))
+#define POLKIT_BACKEND_IS_JS_AUTHORITY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), POLKIT_BACKEND_TYPE_JS_AUTHORITY))
+
+typedef struct _PolkitBackendJsAuthorityClass    PolkitBackendJsAuthorityClass;
+typedef struct _PolkitBackendJsAuthorityPrivate  PolkitBackendJsAuthorityPrivate;
+
+/**
+ * PolkitBackendJsAuthority:
+ *
+ * The #PolkitBackendJsAuthority struct should not be accessed directly.
+ */
+struct _PolkitBackendJsAuthority
+{
+  /*< private >*/
+  PolkitBackendInteractiveAuthority parent_instance;
+  PolkitBackendJsAuthorityPrivate *priv;
+};
+
+/**
+ * PolkitBackendJsAuthorityClass:
+ * @parent_class: The parent class.
+ *
+ * Class structure for #PolkitBackendJsAuthority.
+ */
+struct _PolkitBackendJsAuthorityClass
+{
+  /*< public >*/
+  PolkitBackendInteractiveAuthorityClass parent_class;
+};
+
+GType                   polkit_backend_js_authority_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __POLKIT_BACKEND_JS_AUTHORITY_H */
+
+
diff --git a/src/polkitbackend/polkitbackendtypes.h b/src/polkitbackend/polkitbackendtypes.h
index d06f62a..2fe36ac 100644
--- a/src/polkitbackend/polkitbackendtypes.h
+++ b/src/polkitbackend/polkitbackendtypes.h
@@ -36,5 +36,8 @@ typedef struct _PolkitBackendInteractiveAuthority PolkitBackendInteractiveAuthor
 struct _PolkitBackendLocalAuthority;
 typedef struct _PolkitBackendLocalAuthority PolkitBackendLocalAuthority;
 
+struct _PolkitBackendJsAuthority;
+typedef struct _PolkitBackendJsAuthority PolkitBackendJsAuthority;
+
 #endif /* __POLKIT_BACKEND_TYPES_H */
 
diff --git a/test/data/etc/polkit-1/rules.d/10-testing.rules b/test/data/etc/polkit-1/rules.d/10-testing.rules
new file mode 100644
index 0000000..adf4f16
--- /dev/null
+++ b/test/data/etc/polkit-1/rules.d/10-testing.rules
@@ -0,0 +1,32 @@
+/* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */
+
+polkit.addAdministratorRule(function(action, subject) {
+    return ["unix-group:admin", "unix-user:root"];
+});
+
+polkit.addAdministratorRule(function(action, subject) {
+    if (action == "net.company.action1") {
+        return ["unix-group:admin"];
+    }
+    return null;
+});
+
+polkit.addAdministratorRule(function(action, subject) {
+    if (action == "net.company.action2") {
+        return ["unix-group:users"];
+    }
+    return null;
+});
+
+// -----
+
+polkit.addAuthorizationRule(function(action, subject) {
+    return "auth_admin";
+});
+
+polkit.addAuthorizationRule(function(action, subject) {
+    if (action == "org.freedesktop.policykit.exec") {
+        return "auth_admin";
+    }
+    return null;
+});
diff --git a/test/polkitbackend/Makefile.am b/test/polkitbackend/Makefile.am
index c611b5b..46706d3 100644
--- a/test/polkitbackend/Makefile.am
+++ b/test/polkitbackend/Makefile.am
@@ -39,8 +39,12 @@ polkitbackendlocalauthorizationstoretest_SOURCES = polkitbackendlocalauthorizati
 TEST_PROGS += polkitbackendlocalauthoritytest
 polkitbackendlocalauthoritytest_SOURCES = polkitbackendlocalauthoritytest.c
 
+TEST_PROGS += polkitbackendjsauthoritytest
+polkitbackendjsauthoritytest_SOURCES = test-polkitbackendjsauthority.c
+
 # ----------------------------------------------------------------------------------------------------
 
+noinst_PROGRAMS = $(TEST_PROGS)
 check_PROGRAMS = $(TEST_PROGS)
 TESTS = $(TEST_PROGS)
 
diff --git a/test/polkitbackend/test-polkitbackendjsauthority.c b/test/polkitbackend/test-polkitbackendjsauthority.c
new file mode 100644
index 0000000..c5015ff
--- /dev/null
+++ b/test/polkitbackend/test-polkitbackendjsauthority.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2012 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: Nikki VonHollen <vonhollen at google.com>
+ *         David Zeuthen <davidz at redhat.com>
+ */
+
+#include "glib.h"
+
+#include <polkit/polkit.h>
+#include <polkitbackend/polkitbackendjsauthority.h>
+#include <polkittesthelper.h>
+
+/* Test helper types */
+
+static PolkitBackendJsAuthority *get_authority (void);
+
+static PolkitBackendJsAuthority *
+get_authority (void)
+{
+  gchar *rules_dir;
+  PolkitBackendJsAuthority *authority;
+
+  rules_dir = polkit_test_get_data_path ("etc/polkit-1/rules.d");
+  g_assert (rules_dir != NULL);
+
+  authority = g_object_new (POLKIT_BACKEND_TYPE_JS_AUTHORITY,
+                            "rules-dir", rules_dir,
+                            NULL);
+  g_free (rules_dir);
+  return authority;
+}
+
+
+static void
+test_get_admin_identities_for_action_id (const gchar         *action_id,
+                                         const gchar *const *expected_admins)
+{
+  PolkitBackendJsAuthority *authority = NULL;
+  PolkitSubject *caller = NULL;
+  PolkitSubject *subject = NULL;
+  PolkitIdentity *user_for_subject = NULL;
+  PolkitDetails *details = NULL;
+  GError *error = NULL;
+  GList *admin_identities = NULL;
+  GList *l;
+  guint n;
+
+  authority = get_authority ();
+
+  caller = polkit_unix_process_new (getpid ());
+  subject = polkit_unix_process_new (getpid ());
+  user_for_subject = polkit_identity_from_string ("unix-user:root", &error);
+  g_assert_no_error (error);
+
+  details = polkit_details_new ();
+
+  /* Get the list of PolkitUnixUser objects who are admins */
+  admin_identities = polkit_backend_interactive_authority_get_admin_identities (POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority),
+                                                                                caller,
+                                                                                subject,
+                                                                                user_for_subject,
+                                                                                action_id,
+                                                                                details);
+  for (l = admin_identities, n = 0; l != NULL; l = l->next, n++)
+    {
+      PolkitIdentity *test_identity = POLKIT_IDENTITY (l->data);
+      gchar *s;
+
+      g_assert (expected_admins[n] != NULL);
+
+      s = polkit_identity_to_string (test_identity);
+      g_assert_cmpstr (expected_admins[n], ==, s);
+      g_free (s);
+    }
+  g_assert (expected_admins[n] == NULL);
+
+  g_list_free_full (admin_identities, g_object_unref);
+  g_clear_object (&user_for_subject);
+  g_clear_object (&subject);
+  g_clear_object (&caller);
+  g_clear_object (&authority);
+}
+
+static void
+test_get_admin_identities (void)
+{
+  struct {
+    const gchar *action_id;
+    const gchar *expected_admins[5];
+  } test_cases[] = {
+    {
+      "com.example.doesntmatter",
+      {
+        "unix-group:admin",
+        "unix-user:root"
+      }
+    },
+    {
+      "net.company.action1",
+      {
+        "unix-group:admin"
+      }
+    },
+    {
+      "net.company.action2",
+      {
+        "unix-group:users"
+      }
+    },
+  };
+  guint n;
+
+  for (n = 0; n < G_N_ELEMENTS (test_cases); n++)
+    {
+      test_get_admin_identities_for_action_id (test_cases[n].action_id,
+                                               test_cases[n].expected_admins);
+    }
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  GIOExtensionPoint *ep;
+
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+  //polkit_test_redirect_logs ();
+
+  ep = g_io_extension_point_register (POLKIT_BACKEND_AUTHORITY_EXTENSION_POINT_NAME);
+  g_io_extension_point_set_required_type (ep, POLKIT_BACKEND_TYPE_AUTHORITY);
+
+  g_test_add_func ("/PolkitBackendJsAuthority/get_admin_identities", test_get_admin_identities);
+
+  return g_test_run ();
+};


More information about the hal-commit mailing list