summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--src/polkitbackend/Makefile.am3
-rw-r--r--src/polkitbackend/polkitbackendauthority.c109
-rw-r--r--src/polkitbackend/polkitbackendinteractiveauthority.c132
-rw-r--r--src/polkitbackend/polkitbackendjsauthority.c831
-rw-r--r--src/polkitbackend/polkitbackendjsauthority.h75
-rw-r--r--src/polkitbackend/polkitbackendtypes.h3
-rw-r--r--test/data/etc/polkit-1/rules.d/10-testing.rules32
-rw-r--r--test/polkitbackend/Makefile.am4
-rw-r--r--test/polkitbackend/test-polkitbackendjsauthority.c153
10 files changed, 1340 insertions, 6 deletions
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@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@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@google.com>
+ * David Zeuthen <davidz@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 ();
+};