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

David Zeuthen david at kemper.freedesktop.org
Thu May 24 11:53:19 PDT 2012


 docs/man/polkit.xml                                   |  379 +++++++++---------
 src/polkitbackend/50-default.rules                    |    2 
 src/polkitbackend/init.js                             |   26 -
 src/polkitbackend/polkitbackendjsauthority.c          |  106 +----
 test/data/etc/polkit-1/rules.d/10-testing.rules       |   83 ++-
 test/data/etc/polkit-1/rules.d/15-testing.rules       |   19 
 test/data/usr/share/polkit-1/rules.d/10-testing.rules |   12 
 test/data/usr/share/polkit-1/rules.d/20-testing.rules |   22 -
 test/polkitbackend/test-polkitbackendjsauthority.c    |   85 +++-
 9 files changed, 382 insertions(+), 352 deletions(-)

New commits:
commit 0e85f07781f8eab9670e06cee32b38657e3b62ce
Author: David Zeuthen <davidz at redhat.com>
Date:   Thu May 24 14:51:46 2012 -0400

    Combine action and details parameters
    
    This also removes the ability to change detail parameters which is
    actually a good thing. If we later need a way to change the
    authentication message, we can always add something like
    polkit.addAuthenticationMessageRule() so the user can register a
    function returning a string.
    
    Signed-off-by: David Zeuthen <davidz at redhat.com>

diff --git a/docs/man/polkit.xml b/docs/man/polkit.xml
index de4bb4a..9718541 100644
--- a/docs/man/polkit.xml
+++ b/docs/man/polkit.xml
@@ -484,120 +484,196 @@ System Context         |                        |
       programming language and interface with <command>polkitd</command>
       through the global
       <literal>polkit</literal> object (of type <type>Polkit</type>).
-      The following methods are available:
     </para>
 
-    <funcsynopsis>
-      <funcprototype>
-        <?dbhtml funcsynopsis-style='ansi'?>
-        <funcdef>void <function>addRule</function></funcdef>
-        <paramdef>string <function>function</function>(<parameter>action</parameter>, <parameter>subject</parameter>, <parameter>details</parameter>) {...}</paramdef>
-      </funcprototype>
-    </funcsynopsis>
-
-    <funcsynopsis>
-      <funcprototype>
-        <?dbhtml funcsynopsis-style='ansi'?>
-        <funcdef>void <function>addAdminRule</function></funcdef>
-        <paramdef>string[] <function>function</function>(<parameter>action</parameter>, <parameter>subject</parameter>, <parameter>details</parameter>) {...}</paramdef>
-      </funcprototype>
-    </funcsynopsis>
-
-    <funcsynopsis>
-      <funcprototype>
-        <?dbhtml funcsynopsis-style='ansi'?>
-        <funcdef>void <function>log</function></funcdef>
-        <paramdef>string <parameter>message</parameter></paramdef>
-      </funcprototype>
-    </funcsynopsis>
-
-    <funcsynopsis>
-      <funcprototype>
-        <?dbhtml funcsynopsis-style='ansi'?>
-        <funcdef>string <function>spawn</function></funcdef>
-        <paramdef>string[] <parameter>argv</parameter></paramdef>
-      </funcprototype>
-    </funcsynopsis>
+    <refsect2 id="polkit-rules-actions">
+      <title>The <type>Polkit</type> type</title>
 
-    <para>
-      The <function>addRule()</function> method is used for adding a
-      function that may be called whenever an authorization check for
-      <parameter>action</parameter>, <parameter>subject</parameter>
-      and <parameter>details</parameter> is performed. Functions are
-      called in the order they have been added until one of the
-      functions returns a value. Hence, to add an authorization rule
-      that is processed before other rules, put it in a file in
-      <filename class='directory'>/etc/polkit-1/rules.d</filename>
-      with a name that sorts before other rules files, for example
-      <filename>00-early-checks.rules</filename>. Each function should
-      return one of the values <literal>"no"</literal>,
-      <literal>"yes"</literal>, <literal>"auth_self"</literal>,
-      <literal>"auth_self_keep"</literal>,
-      <literal>"auth_admin"</literal>,
-      <literal>"auth_admin_keep"</literal> as defined above. If the
-      function returns <constant>null</constant>,
-      <constant>undefined</constant> or does not return a value at
-      all, the next function is tried.
-    </para>
+      <para>
+        The following methods are available on the <literal>polkit</literal> object:
+      </para>
 
-    <para>
-      The <function>addAdminRule()</function> method is used for
-      adding a function may be called whenever administrator
-      authentication is required. The function is used to specify what
-      identies may be used for administrator authentication for the
-      authorization check identified by <parameter>action</parameter>,
-      <parameter>subject</parameter> and
-      <parameter>details</parameter>. Functions added are called in
-      the order they have been added until one of the functions
-      returns a value. Each function should return an array of strings
-      where each string is of the form
-      <literal>"unix-group:&lt;group&gt;"</literal>,
-      <literal>"unix-netgroup:&lt;netgroup&gt;"</literal> or
-      <literal>"unix-user:&lt;user&gt;"</literal>.  If the function
-      returns <constant>null</constant>,
-      <constant>undefined</constant> or does not return a value at
-      all, the next function is tried.
-    </para>
+      <funcsynopsis>
+        <funcprototype>
+          <?dbhtml funcsynopsis-style='ansi'?>
+          <funcdef>void <function>addRule</function></funcdef>
+          <paramdef>string <function>function</function>(<parameter>action</parameter>, <parameter>subject</parameter>) {...}</paramdef>
+        </funcprototype>
+      </funcsynopsis>
 
-    <para>
-      There is no guarantee that a function registered with
-      <function>addRule()</function> or
-      <function>addAdminRule()</function> is ever called - for example
-      an early rules file could register a function that always return
-      a value, hence ensuring that functions added later are never
-      called.
-    </para>
+      <funcsynopsis>
+        <funcprototype>
+          <?dbhtml funcsynopsis-style='ansi'?>
+          <funcdef>void <function>addAdminRule</function></funcdef>
+          <paramdef>string[] <function>function</function>(<parameter>action</parameter>, <parameter>subject</parameter>) {...}</paramdef>
+        </funcprototype>
+      </funcsynopsis>
 
-    <para>
-      If user-provided code takes a long time to execute an exception
-      will be thrown which normally results in the function being
-      terminated (the current limit is 15 seconds). This is used to
-      catch runaway scripts.
-    </para>
+      <funcsynopsis>
+        <funcprototype>
+          <?dbhtml funcsynopsis-style='ansi'?>
+          <funcdef>void <function>log</function></funcdef>
+          <paramdef>string <parameter>message</parameter></paramdef>
+        </funcprototype>
+      </funcsynopsis>
 
-    <para>
-      The <function>log()</function> method writes the given
-      <parameter>message</parameter> to the system logger. Log entries
-      are emitted using the <constant>LOG_AUTHPRIV</constant> flag
-      meaning that the log entries usually ends up in the file
-      <filename>/var/log/secure</filename>. The
-      <function>log()</function> method is usually only used when
-      debugging rules.
-    </para>
+      <funcsynopsis>
+        <funcprototype>
+          <?dbhtml funcsynopsis-style='ansi'?>
+          <funcdef>string <function>spawn</function></funcdef>
+          <paramdef>string[] <parameter>argv</parameter></paramdef>
+        </funcprototype>
+      </funcsynopsis>
 
-    <para>
-      The <function>spawn()</function> method spawns an external
-      helper identified by the argument vector
-      <parameter>argv</parameter> and waits for it to terminate. If an
-      error occurs or the helper doesn't exit normally with exit code
-      0, an exception is thrown. If the helper does not exit within 10
-      seconds it is killed. Otherwise, the program's
-      <emphasis>standard output</emphasis> is returned as a string.
-      The <function>spawn()</function> method should be used sparingly
-      as helpers may take a very long or indeterminate amount of time
-      to complete and no other authorization check can be handled
-      while the helper is running.
-    </para>
+      <para>
+        The <function>addRule()</function> method is used for adding a
+        function that may be called whenever an authorization check for
+        <parameter>action</parameter> and <parameter>subject</parameter>
+        is performed. Functions are
+        called in the order they have been added until one of the
+        functions returns a value. Hence, to add an authorization rule
+        that is processed before other rules, put it in a file in
+        <filename class='directory'>/etc/polkit-1/rules.d</filename>
+        with a name that sorts before other rules files, for example
+        <filename>00-early-checks.rules</filename>. Each function should
+        return one of the values <literal>"no"</literal>,
+        <literal>"yes"</literal>, <literal>"auth_self"</literal>,
+        <literal>"auth_self_keep"</literal>,
+        <literal>"auth_admin"</literal>,
+        <literal>"auth_admin_keep"</literal> as defined above. If the
+        function returns <constant>null</constant>,
+        <constant>undefined</constant> or does not return a value at
+        all, the next function is tried.
+      </para>
+
+      <para>
+        The <function>addAdminRule()</function> method is used for
+        adding a function may be called whenever administrator
+        authentication is required. The function is used to specify what
+        identies may be used for administrator authentication for the
+        authorization check identified by <parameter>action</parameter>
+        and <parameter>subject</parameter>. Functions added are called in
+        the order they have been added until one of the functions
+        returns a value. Each function should return an array of strings
+        where each string is of the form
+        <literal>"unix-group:&lt;group&gt;"</literal>,
+        <literal>"unix-netgroup:&lt;netgroup&gt;"</literal> or
+        <literal>"unix-user:&lt;user&gt;"</literal>.  If the function
+        returns <constant>null</constant>,
+        <constant>undefined</constant> or does not return a value at
+        all, the next function is tried.
+      </para>
+
+      <para>
+        There is no guarantee that a function registered with
+        <function>addRule()</function> or
+        <function>addAdminRule()</function> is ever called - for example
+        an early rules file could register a function that always return
+        a value, hence ensuring that functions added later are never
+        called.
+      </para>
+
+      <para>
+        If user-provided code takes a long time to execute an exception
+        will be thrown which normally results in the function being
+        terminated (the current limit is 15 seconds). This is used to
+        catch runaway scripts.
+      </para>
+
+      <para>
+        The <function>spawn()</function> method spawns an external
+        helper identified by the argument vector
+        <parameter>argv</parameter> and waits for it to terminate. If an
+        error occurs or the helper doesn't exit normally with exit code
+        0, an exception is thrown. If the helper does not exit within 10
+        seconds it is killed. Otherwise, the program's
+        <emphasis>standard output</emphasis> is returned as a string.
+        The <function>spawn()</function> method should be used sparingly
+        as helpers may take a very long or indeterminate amount of time
+        to complete and no other authorization check can be handled
+        while the helper is running.
+      </para>
+
+      <para>
+        The <function>log()</function> method writes the given
+        <parameter>message</parameter> to the system logger prefixed
+        with the JavaScript filename and line number. Log entries are
+        emitted using the <constant>LOG_AUTHPRIV</constant> flag meaning
+        that the log entries usually ends up in the file
+        <filename>/var/log/secure</filename>. The
+        <function>log()</function> method is usually only used when
+        debugging rules. The <type>Action</type> and
+        <type>Subject</type> types has suitable
+        <function>toString()</function> methods defined for easy
+        logging, for example,
+      </para>
+      <programlisting><![CDATA[
+polkit.addRule(function(action, subject) {
+    if (action.id == "org.freedesktop.policykit.exec") {
+        polkit.log("action=" + action);
+        polkit.log("subject=" + subject);
+    }
+});
+]]></programlisting>
+      <para>
+        will produce the following when the user runs 'pkexec bash -i' from a shelll:
+      </para>
+      <programlisting><![CDATA[
+May 24 14:28:50 thinkpad polkitd[32217]: /etc/polkit-1/rules.d/10-test.rules:3: action=[Action id='org.freedesktop.policykit.exec' command_line='/usr/bin/bash -i' program='/usr/bin/bash' user_full='root (root)' user='root']
+May 24 14:28:50 thinkpad polkitd[32217]: /etc/polkit-1/rules.d/10-test.rules:4: subject=[Subject pid=1352 user='davidz' groups=davidz,wheel, seat='seat0' session='1' local=true active=true]
+]]></programlisting>
+
+    </refsect2>
+
+    <refsect2 id="polkit-rules-actions">
+      <title>The <type>Action</type> type</title>
+
+      <para>
+        The <parameter>action</parameter> parameter passed to user
+        functions is an object with information about the action
+        being checked. It is of type <type>Action</type> and has
+        the following attribute:
+      </para>
+
+      <variablelist id="polkit-js-action-attributes">
+        <varlistentry>
+          <term><type>string</type> id</term>
+          <listitem>
+            <para>
+              The action identifier, for example
+              <emphasis>org.freedesktop.policykit.exec</emphasis>.
+            </para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>
+        The following methods are available on the <type>Action</type> type:
+      </para>
+
+      <funcsynopsis>
+        <funcprototype>
+          <?dbhtml funcsynopsis-style='ansi'?>
+          <funcdef>string <function>lookup</function></funcdef>
+          <paramdef>string <parameter>key</parameter></paramdef>
+        </funcprototype>
+      </funcsynopsis>
+
+      <para>
+        The <function>lookup()</function> method is used to lookup the
+        polkit variables passed from the mechanism. For example, the
+        <link linkend="pkexec.1"><citerefentry><refentrytitle>pkexec</refentrytitle><manvolnum>1</manvolnum></citerefentry></link>
+        mechanism sets the variable <parameter>program</parameter>
+        which can be obtained in Javascript using the expression
+        <literal>action.lookup("program")</literal>. If there is
+        no value for the given <parameter>key</parameter>,
+        then <constant>undefined</constant> is returned.
+      </para>
+      <para>
+        Consult the documentation for each mechanism for what
+        variables are available for each action.
+      </para>
+    </refsect2>
 
     <refsect2 id="polkit-rules-subject">
       <title>The <type>Subject</type> type</title>
@@ -700,52 +776,6 @@ System Context         |                        |
         <function>isInNetGroup()</function> can be used to check if
         the subject is in a given netgroup.
       </para>
-
-    </refsect2>
-
-    <refsect2 id="polkit-rules-details">
-      <title>The <type>Details</type> type</title>
-
-      <para>
-        The <parameter>details</parameter> parameter passed to user
-        functions is an object with more information about the action
-        being checked. It is of type <type>Details</type> and has
-        details being set by the mechanism as attributes. For example,
-        the <link linkend="pkexec.1"><citerefentry><refentrytitle>pkexec</refentrytitle><manvolnum>1</manvolnum></citerefentry></link>
-        mechanism sets the variable <parameter>program</parameter>
-        which can be obtained in Javascript using the expression
-        <literal>details["program"]</literal>. Consult the
-        documentation for each mechanism for what variables are
-        available for each action.
-      </para>
-
-      <para>
-        The <parameter>details</parameter> also has the following
-        well-known attributes:
-      </para>
-      <variablelist>
-        <varlistentry>
-          <term><emphasis>polkit.message</emphasis></term>
-          <listitem>
-            <para>
-              The message to show in the authentication dialog (only
-              used if authentication is needed). Its initial value is
-              taken from the action declaration (the <literal>message</literal> element in the <filename
-              class='extension'>.policy</filename> file) but the value
-              can be overridden by the mechanism setting this key in
-              the <parameter>details</parameter> passed when doing the
-              <link
-              linkend="eggdbus-method-org.freedesktop.PolicyKit1.Authority.CheckAuthorization">CheckAuthorization()</link>
-              call.
-            </para>
-          </listitem>
-        </varlistentry>
-      </variablelist>
-      <para>
-        Note that a rule can set the
-        <parameter>polkit.message</parameter> attribute to change the
-        message shown in the authentication dialog.
-      </para>
     </refsect2>
 
     <refsect2 id="polkit-rules-examples">
@@ -757,8 +787,8 @@ System Context         |                        |
         users:
       </para>
       <programlisting><![CDATA[
-polkit.addRule(function(action, subject, details) {
-    if (action == "org.freedesktop.accounts.user-administration" &&
+polkit.addRule(function(action, subject) {
+    if (action.id == "org.freedesktop.accounts.user-administration" &&
         subject.isInGroup("admin")) {
         return "yes";
     }
@@ -769,20 +799,21 @@ polkit.addRule(function(action, subject, details) {
         Define administrative users to be the users in the <literal>wheel</literal> group:
       </para>
       <programlisting><![CDATA[
-polkit.addAdminRule(function(action, subject, details) {
+polkit.addAdminRule(function(action, subject) {
     return ["unix-group:wheel"];
 });
 ]]></programlisting>
 
       <para>
         Forbid users in group <literal>children</literal> to change
-        hostname configuration (that is, any action starting with
-        <literal>org.freedesktop.hostname1.</literal>) and allow
-        anyone else to do it after authenticating as themselves:
+        hostname configuration (that is, any action with an identifier
+        starting with <literal>org.freedesktop.hostname1.</literal>)
+        and allow anyone else to do it after authenticating as
+        themselves:
       </para>
       <programlisting><![CDATA[
-polkit.addRule(function(action, subject, details) {
-    if (action.indexOf("org.freedesktop.hostname1.") == 0) {
+polkit.addRule(function(action, subject) {
+    if (action.id.indexOf("org.freedesktop.hostname1.") == 0) {
         if (subject.isInGroup("children")) {
             return "no";
         } else {
@@ -796,8 +827,8 @@ polkit.addRule(function(action, subject, details) {
         Run an external helper to determine if the current user may reboot the system:
       </para>
       <programlisting><![CDATA[
-polkit.addRule(function(action, subject, details) {
-    if (action.indexOf("org.freedesktop.login1.reboot") == 0) {
+polkit.addRule(function(action, subject) {
+    if (action.id.indexOf("org.freedesktop.login1.reboot") == 0) {
         try {
             // user-may-reboot exits with succeess (exit code 0)
             // only if the passed username is authorized
@@ -813,17 +844,15 @@ polkit.addRule(function(action, subject, details) {
 ]]></programlisting>
 
       <para>
-        The following example showcases two things
+        The following example shows how the authorization decision
+        can depend on variables passed by the
+        <link linkend="pkexec.1"><citerefentry><refentrytitle>pkexec</refentrytitle><manvolnum>1</manvolnum></citerefentry></link>
+        mechanism:
       </para>
-      <itemizedlist mark='opencircle' spacing='compact'>
-        <listitem><para>how the authorization decision can depend on variables passed by the mechanism</para></listitem>
-        <listitem><para>how to override the message shown in the authentication dialog</para></listitem>
-      </itemizedlist>
       <programlisting><![CDATA[
-polkit.addRule(function(action, subject, details) {
-    if (action == "org.freedesktop.policykit.exec" &&
-        details["program"] == "/usr/bin/cat") {
-        details["polkit.message"] = "Achtung! You need to authenticate as yourself to cat(1) files!";
+polkit.addRule(function(action, subject) {
+    if (action.id == "org.freedesktop.policykit.exec" &&
+        action.lookup("program") == "/usr/bin/cat") {
         return "auth_self";
     }
 });
@@ -843,10 +872,10 @@ polkit.addRule(function(action, subject, details) {
 // Allow users in group 'engineers' to perform any operation on
 // some drives without having to authenticate
 //
-polkit.addRule(function(action, subject, details) {
-    if (action.indexOf("org.freedesktop.udisks2.") == 0 &&
-        details["drive.vendor"] == "SEAGATE" &&
-        details["drive.model"] == "ST3300657SS" &&
+polkit.addRule(function(action, subject) {
+    if (action.id.indexOf("org.freedesktop.udisks2.") == 0 &&
+        action.lookup("drive.vendor") == "SEAGATE" &&
+        action.lookup("drive.model") == "ST3300657SS" &&
         subject.isInGroup("engineers")) {
             return "yes";
         }
diff --git a/src/polkitbackend/50-default.rules b/src/polkitbackend/50-default.rules
index 9d3c33d..f427ae1 100644
--- a/src/polkitbackend/50-default.rules
+++ b/src/polkitbackend/50-default.rules
@@ -7,6 +7,6 @@
 // See the polkit(8) man page for more information
 // about configuring polkit.
 
-polkit.addAdminRule(function(action, subject, details) {
+polkit.addAdminRule(function(action, subject) {
     return ["unix-group:wheel"];
 });
diff --git a/src/polkitbackend/init.js b/src/polkitbackend/init.js
index 29f13fc..16862d4 100644
--- a/src/polkitbackend/init.js
+++ b/src/polkitbackend/init.js
@@ -1,14 +1,17 @@
 /* -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- */
 
-function Details() {
+function Action() {
+    this.lookup = function(name) {
+        return this["_detail_" + name];
+    },
+
     this.toString = function() {
-        var ret = "[Details";
+        var ret = "[Action id='" + this.id + "'";
         for (var i in this) {
-            if (typeof this[i] != "function") {
-                if (typeof this[i] == "string")
-                    ret += " " + i + "='" + this[i] + "'";
-                else
-                    ret += " " + i + "=" + this[i];
+            if (i.indexOf("_detail_") == 0) {
+                var key = i.substr(8);
+                var value = this[i];
+                ret += " " + key + "='" + value + "'";
             }
         }
         ret += "]";
@@ -17,7 +20,6 @@ function Details() {
 };
 
 function Subject() {
-
     this.isInGroup = function(group) {
         for (var n = 0; n < this.groups.length; n++) {
             if (this.groups[n] == group)
@@ -47,11 +49,11 @@ function Subject() {
 
 polkit._adminRuleFuncs = [];
 polkit.addAdminRule = function(callback) {this._adminRuleFuncs.push(callback);};
-polkit._runAdminRules = function(action, subject, details) {
+polkit._runAdminRules = function(action, subject) {
     var ret = null;
     for (var n = 0; n < this._adminRuleFuncs.length; n++) {
         var func = this._adminRuleFuncs[n];
-        var func_ret = func(action, subject, details);
+        var func_ret = func(action, subject);
         if (func_ret) {
             ret = func_ret;
             break
@@ -62,11 +64,11 @@ polkit._runAdminRules = function(action, subject, details) {
 
 polkit._ruleFuncs = [];
 polkit.addRule = function(callback) {this._ruleFuncs.push(callback);};
-polkit._runRules = function(action, subject, details) {
+polkit._runRules = function(action, subject) {
     var ret = null;
     for (var n = 0; n < this._ruleFuncs.length; n++) {
         var func = this._ruleFuncs[n];
-        var func_ret = func(action, subject, details);
+        var func_ret = func(action, subject);
         if (func_ret) {
             ret = func_ret;
             break
diff --git a/src/polkitbackend/polkitbackendjsauthority.c b/src/polkitbackend/polkitbackendjsauthority.c
index cc805e8..d71f85f 100644
--- a/src/polkitbackend/polkitbackendjsauthority.c
+++ b/src/polkitbackend/polkitbackendjsauthority.c
@@ -806,10 +806,11 @@ subject_to_jsval (PolkitBackendJsAuthority  *authority,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static gboolean
-details_to_jsval (PolkitBackendJsAuthority  *authority,
-                  PolkitDetails             *details,
-                  jsval                     *out_jsval,
-                  GError                   **error)
+action_and_details_to_jsval (PolkitBackendJsAuthority  *authority,
+                             const gchar               *action_id,
+                             PolkitDetails             *details,
+                             jsval                     *out_jsval,
+                             GError                   **error)
 {
   gboolean ret = FALSE;
   jsval ret_jsval;
@@ -818,8 +819,7 @@ details_to_jsval (PolkitBackendJsAuthority  *authority,
   gchar **keys;
   guint n;
 
-  src = "new Details();";
-
+  src = "new Action();";
   if (!JS_EvaluateScript (authority->priv->cx,
                           authority->priv->js_global,
                           src, strlen (src),
@@ -831,18 +831,18 @@ details_to_jsval (PolkitBackendJsAuthority  *authority,
     }
 
   obj = JSVAL_TO_OBJECT (ret_jsval);
+
+  set_property_str (authority, obj, "id", action_id);
+
   keys = polkit_details_get_keys (details);
   for (n = 0; keys != NULL && keys[n] != NULL; n++)
     {
-      const gchar *key = keys[n];
-      JSString *value_jsstr;
-      jsval value_jsval;
+      gchar *key;
       const gchar *value;
-
+      key = g_strdup_printf ("_detail_%s", keys[n]);
       value = polkit_details_lookup (details, keys[n]);
-      value_jsstr = JS_NewStringCopyZ (authority->priv->cx, value);
-      value_jsval = STRING_TO_JSVAL (value_jsstr);
-      JS_SetProperty (authority->priv->cx, obj, key, &value_jsval);
+      set_property_str (authority, obj, key, value);
+      g_free (key);
     }
   g_free (keys);
 
@@ -990,31 +990,27 @@ polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveA
 {
   PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
   GList *ret = NULL;
-  jsval argv[3] = {0};
+  jsval argv[2] = {0};
   jsval rval = {0};
-  JSString *action_id_jstr;
   guint n;
   GError *error = NULL;
   JSString *ret_jsstr;
   gchar *ret_str = NULL;
   gchar **ret_strs = NULL;
 
-  action_id_jstr = JS_NewStringCopyZ (authority->priv->cx, action_id);
-  argv[0] = STRING_TO_JSVAL (action_id_jstr);
-
-  if (!subject_to_jsval (authority, subject, user_for_subject, &argv[1], &error))
+  if (!action_and_details_to_jsval (authority, action_id, details, &argv[0], &error))
     {
       polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                    "Error converting subject to JS object: %s",
+                                    "Error converting action and details to JS object: %s",
                                     error->message);
       g_clear_error (&error);
       goto out;
     }
 
-  if (!details_to_jsval (authority, details, &argv[2], &error))
+  if (!subject_to_jsval (authority, subject, user_for_subject, &argv[1], &error))
     {
       polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                    "Error converting details to JS object: %s",
+                                    "Error converting subject to JS object: %s",
                                     error->message);
       g_clear_error (&error);
       goto out;
@@ -1022,7 +1018,7 @@ polkit_backend_js_authority_get_admin_auth_identities (PolkitBackendInteractiveA
 
   if (!call_js_function_with_runaway_killer (authority,
                                              "_runAdminRules",
-                                             3,
+                                             2,
                                              argv,
                                              &rval))
     {
@@ -1093,34 +1089,27 @@ polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAu
 {
   PolkitBackendJsAuthority *authority = POLKIT_BACKEND_JS_AUTHORITY (_authority);
   PolkitImplicitAuthorization ret = implicit;
-  jsval argv[3] = {0};
+  jsval argv[2] = {0};
   jsval rval = {0};
-  JSString *action_id_jstr;
   GError *error = NULL;
   JSString *ret_jsstr;
   const jschar *ret_utf16;
   gchar *ret_str = NULL;
   gboolean good = FALSE;
-  JSIdArray *ids;
-  JSObject *details_obj;
-  gint n;
 
-  action_id_jstr = JS_NewStringCopyZ (authority->priv->cx, action_id);
-  argv[0] = STRING_TO_JSVAL (action_id_jstr);
-
-  if (!subject_to_jsval (authority, subject, user_for_subject, &argv[1], &error))
+  if (!action_and_details_to_jsval (authority, action_id, details, &argv[0], &error))
     {
       polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                    "Error converting subject to JS object: %s",
+                                    "Error converting action and details to JS object: %s",
                                     error->message);
       g_clear_error (&error);
       goto out;
     }
 
-  if (!details_to_jsval (authority, details, &argv[2], &error))
+  if (!subject_to_jsval (authority, subject, user_for_subject, &argv[1], &error))
     {
       polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                    "Error converting details to JS object: %s",
+                                    "Error converting subject to JS object: %s",
                                     error->message);
       g_clear_error (&error);
       goto out;
@@ -1169,53 +1158,6 @@ polkit_backend_js_authority_check_authorization_sync (PolkitBackendInteractiveAu
       goto out;
     }
 
-
-  /* the JS code may have modifed @details - update PolkitDetails
-   * object accordingly
-   */
-  details_obj = JSVAL_TO_OBJECT (argv[2]);
-  ids = JS_Enumerate (authority->priv->cx, details_obj);
-  if (ids == NULL)
-    {
-      polkit_backend_authority_log (POLKIT_BACKEND_AUTHORITY (authority),
-                                    "Failed to enumerate properties of Details object");
-      goto out;
-    }
-  for (n = 0; n < ids->length; n++)
-    {
-      jsval id_val;
-      jsval value_val;
-      char *id_s = NULL;
-      char *value_s = NULL;
-
-      if (!JS_IdToValue (authority->priv->cx, ids->vector[n], &id_val))
-        {
-          g_warning ("Error getting string for property id %d", n);
-          goto cont;
-        }
-      id_s = JS_EncodeString (authority->priv->cx, JSVAL_TO_STRING (id_val));
-
-      if (!JS_GetPropertyById (authority->priv->cx, details_obj, ids->vector[n], &value_val))
-        {
-          g_warning ("Error getting value string for property value %s", id_s);
-          goto cont;
-        }
-
-      /* skip e.g. functions */
-      if (!JSVAL_IS_STRING (value_val) && !JSVAL_IS_NULL (value_val))
-        goto cont;
-
-      value_s = JS_EncodeString (authority->priv->cx, JSVAL_TO_STRING (value_val));
-
-      polkit_details_insert (details, id_s, value_s);
-    cont:
-      if (id_s != NULL)
-        JS_free (authority->priv->cx, id_s);
-      if (value_s != NULL)
-        JS_free (authority->priv->cx, value_s);
-    }
-  JS_DestroyIdArray (authority->priv->cx, ids);
-
   good = TRUE;
 
  out:
diff --git a/test/data/etc/polkit-1/rules.d/10-testing.rules b/test/data/etc/polkit-1/rules.d/10-testing.rules
index 1dba38a..4a17f8c 100644
--- a/test/data/etc/polkit-1/rules.d/10-testing.rules
+++ b/test/data/etc/polkit-1/rules.d/10-testing.rules
@@ -4,55 +4,75 @@
 
 /* NOTE: this is the /etc/polkit-1/rules.d version of 10-testing.rules */
 
-polkit.addAdminRule(function(action, subject, details) {
-    if (action == "net.company.action1") {
+// ---------------------------------------------------------------------
+// admin rules
+
+polkit.addAdminRule(function(action, subject) {
+    if (action.id == "net.company.action1") {
         return ["unix-group:admin"];
     }
 });
 
-polkit.addAdminRule(function(action, subject, details) {
-    if (action == "net.company.action2") {
+polkit.addAdminRule(function(action, subject) {
+    if (action.id == "net.company.action2") {
         return ["unix-group:users"];
     }
 });
 
-polkit.addAdminRule(function(action, subject, details) {
-    if (action == "net.company.action3") {
+polkit.addAdminRule(function(action, subject) {
+    if (action.id == "net.company.action3") {
         return ["unix-netgroup:foo"];
     }
 });
 
 // Fallback
-polkit.addAdminRule(function(action, subject, details) {
+polkit.addAdminRule(function(action, subject) {
     return ["unix-group:admin", "unix-user:root"];
 });
 
 // -----
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.productA.action0") {
+// ---------------------------------------------------------------------
+// basics
+
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.productA.action0") {
         return "auth_admin";
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.productA.action1") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.productA.action1") {
         return "auth_self";
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order0") {
-        details["test_detail"] = "a";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order0") {
         return "yes";
     }
 });
 
 // ---------------------------------------------------------------------
+// variables
+
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.group.variables") {
+        if (action.lookup("foo") == "1")
+            return "yes";
+        else if (action.lookup("foo") == "2")
+            return "auth_self";
+        else
+            return "auth_admin";
+    }
+});
+
+
+// ---------------------------------------------------------------------
 // group membership
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.group.only_group_users") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.group.only_group_users") {
         if (subject.isInGroup("users"))
             return "yes";
         else
@@ -63,8 +83,8 @@ polkit.addRule(function(action, subject, details) {
 // ---------------------------------------------------------------------
 // netgroup membership
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.group.only_netgroup_users") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.group.only_netgroup_users") {
         if (subject.isInNetGroup("foo"))
             return "yes";
         else
@@ -75,8 +95,8 @@ polkit.addRule(function(action, subject, details) {
 // ---------------------------------------------------------------------
 // spawning
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.spawning.non_existing_helper") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.spawning.non_existing_helper") {
         try {
             polkit.spawn(["/path/to/non/existing/helper"]);
             return "no";
@@ -86,8 +106,8 @@ polkit.addRule(function(action, subject, details) {
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.spawning.successful_helper") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.spawning.successful_helper") {
         try {
             polkit.spawn(["/bin/true"]);
             return "yes";
@@ -97,8 +117,8 @@ polkit.addRule(function(action, subject, details) {
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.spawning.failing_helper") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.spawning.failing_helper") {
         try {
             polkit.spawn(["/bin/false"]);
             return "no";
@@ -108,8 +128,8 @@ polkit.addRule(function(action, subject, details) {
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.spawning.helper_with_output") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.spawning.helper_with_output") {
         try {
             var out = polkit.spawn(["echo", "-n", "-e", "Hello\nWorld"]);
             if (out == "Hello\nWorld")
@@ -122,8 +142,8 @@ polkit.addRule(function(action, subject, details) {
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.spawning.helper_timeout") {
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.spawning.helper_timeout") {
         try {
             polkit.spawn(["sleep", "20"]);
             return "no";
@@ -135,8 +155,11 @@ polkit.addRule(function(action, subject, details) {
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.run_away_script") {
+// ---------------------------------------------------------------------
+// runaway scripts
+
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.run_away_script") {
         try {
             // The following code will never terminate so the runaway
             // script killer will step in after 15 seconds and throw
diff --git a/test/data/etc/polkit-1/rules.d/15-testing.rules b/test/data/etc/polkit-1/rules.d/15-testing.rules
index 9968aa7..b64d731 100644
--- a/test/data/etc/polkit-1/rules.d/15-testing.rules
+++ b/test/data/etc/polkit-1/rules.d/15-testing.rules
@@ -2,23 +2,20 @@
 
 /* see test/polkitbackend/test-polkitbackendjsauthority.c */
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order0") {
-        details["test_detail"] = "c";
-        return "yes";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order0") {
+        return "no"; // earlier rule should win
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order1") {
-        details["test_detail"] = "c";
-        return "yes";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order1") {
+        return "no"; // earlier rule should win
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order2") {
-        details["test_detail"] = "c";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order2") {
         return "yes";
     }
 });
diff --git a/test/data/usr/share/polkit-1/rules.d/10-testing.rules b/test/data/usr/share/polkit-1/rules.d/10-testing.rules
index 48c4957..c60e262 100644
--- a/test/data/usr/share/polkit-1/rules.d/10-testing.rules
+++ b/test/data/usr/share/polkit-1/rules.d/10-testing.rules
@@ -4,16 +4,14 @@
 
 /* NOTE: this is the /usr/share/polkit-1/rules.d version of 10-testing.rules */
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order0") {
-        details["test_detail"] = "c";
-        return "yes";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order0") {
+        return "no"; // earlier rule should win
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order1") {
-        details["test_detail"] = "b";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order1") {
         return "yes";
     }
 });
diff --git a/test/data/usr/share/polkit-1/rules.d/20-testing.rules b/test/data/usr/share/polkit-1/rules.d/20-testing.rules
index 16dd039..5c5bb2c 100644
--- a/test/data/usr/share/polkit-1/rules.d/20-testing.rules
+++ b/test/data/usr/share/polkit-1/rules.d/20-testing.rules
@@ -2,24 +2,20 @@
 
 /* see test/polkitbackend/test-polkitbackendjsauthority.c */
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order0") {
-        polkit.log("blabla");
-        details["test_detail"] = "d";
-        return "yes";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order0") {
+        return "no"; // earlier rule should win
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order1") {
-        details["test_detail"] = "d";
-        return "yes";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order1") {
+        return "no"; // earlier rule should win
     }
 });
 
-polkit.addRule(function(action, subject, details) {
-    if (action == "net.company.order2") {
-        details["test_detail"] = "d";
-        return "yes";
+polkit.addRule(function(action, subject) {
+    if (action.id == "net.company.order2") {
+        return "no"; // earlier rule should win
     }
 });
diff --git a/test/polkitbackend/test-polkitbackendjsauthority.c b/test/polkitbackend/test-polkitbackendjsauthority.c
index 728b433..0a5d0e8 100644
--- a/test/polkitbackend/test-polkitbackendjsauthority.c
+++ b/test/polkitbackend/test-polkitbackendjsauthority.c
@@ -24,6 +24,8 @@
 #include "glib.h"
 
 #include <locale.h>
+#include <string.h>
+
 #include <polkit/polkit.h>
 #include <polkitbackend/polkitbackendjsauthority.h>
 #include <polkittesthelper.h>
@@ -156,8 +158,8 @@ struct RulesTestCase
   const gchar *test_name;
   const gchar *action_id;
   const gchar *identity;
+  const gchar *vars;
   PolkitImplicitAuthorization expected_result;
-  const gchar *expected_detail;
 };
 
 static const RulesTestCase rules_test_cases[] = {
@@ -166,15 +168,15 @@ static const RulesTestCase rules_test_cases[] = {
     "basic0",
     "net.company.productA.action0",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED,
-    NULL
   },
   {
     "basic1",
     "net.company.productA.action1",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED,
-    NULL
   },
 
   /* Ordering tests ... we have four rules files, check they are
@@ -192,24 +194,47 @@ static const RulesTestCase rules_test_cases[] = {
     "order0",
     "net.company.order0",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    "a"
   },
   {
     /* defined in file b, c, d - should pick file b */
     "order1",
     "net.company.order1",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    "b"
   },
   {
     /* defined in file c, d - should pick file c */
     "order2",
     "net.company.order2",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    "c"
+  },
+
+  /* variables */
+  {
+    "variables1",
+    "net.company.group.variables",
+    "unix-user:root",
+    "foo=1",
+    POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
+  },
+  {
+    "variables2",
+    "net.company.group.variables",
+    "unix-user:root",
+    "foo=2",
+    POLKIT_IMPLICIT_AUTHORIZATION_AUTHENTICATION_REQUIRED,
+  },
+  {
+    "variables3",
+    "net.company.group.variables",
+    "unix-user:root",
+    NULL,
+    POLKIT_IMPLICIT_AUTHORIZATION_ADMINISTRATOR_AUTHENTICATION_REQUIRED,
   },
 
   /* check group membership */
@@ -218,16 +243,16 @@ static const RulesTestCase rules_test_cases[] = {
     "group_membership_with_member",
     "net.company.group.only_group_users",
     "unix-user:john",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
   {
     /* sally is not a member of group 'users', see test/etc/group */
     "group_membership_with_non_member",
     "net.company.group.only_group_users",
     "unix-user:sally",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
-    NULL
   },
 
   /* check netgroup membership */
@@ -236,16 +261,16 @@ static const RulesTestCase rules_test_cases[] = {
     "netgroup_membership_with_member",
     "net.company.group.only_netgroup_users",
     "unix-user:john",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
   {
     /* sally is not a member of netgroup 'foo', see test/etc/netgroup */
     "netgroup_membership_with_non_member",
     "net.company.group.only_netgroup_users",
     "unix-user:sally",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
-    NULL
   },
 
   /* spawning */
@@ -253,43 +278,45 @@ static const RulesTestCase rules_test_cases[] = {
     "spawning_non_existing_helper",
     "net.company.spawning.non_existing_helper",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
   {
     "spawning_successful_helper",
     "net.company.spawning.successful_helper",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
   {
     "spawning_failing_helper",
     "net.company.spawning.failing_helper",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
   {
     "spawning_helper_with_output",
     "net.company.spawning.helper_with_output",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
   {
-    "runaway_script",
-    "net.company.run_away_script",
+    "spawning_helper_timeout",
+    "net.company.spawning.helper_timeout",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
+
+  /* runaway scripts */
   {
-    "spawning_helper_timeout",
-    "net.company.spawning.helper_timeout",
+    "runaway_script",
+    "net.company.run_away_script",
     "unix-user:root",
+    NULL,
     POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
-    NULL
   },
 };
 
@@ -316,6 +343,23 @@ rules_test_func (gconstpointer user_data)
 
   details = polkit_details_new ();
 
+  if (tc->vars != NULL)
+    {
+      gchar *s;
+      const gchar *key;
+      const gchar *value;
+
+      s = g_strdup (tc->vars);
+      key = s;
+      value = strchr (key, '=');
+      g_assert (value != NULL);
+      *((gchar *) value) = '\0';
+      value += 1;
+
+      polkit_details_insert (details, key, value);
+      g_free (s);
+    }
+
   result = polkit_backend_interactive_authority_check_authorization_sync (POLKIT_BACKEND_INTERACTIVE_AUTHORITY (authority),
                                                                           caller,
                                                                           subject,
@@ -326,7 +370,6 @@ rules_test_func (gconstpointer user_data)
                                                                           details,
                                                                           POLKIT_IMPLICIT_AUTHORIZATION_UNKNOWN);
   g_assert_cmpint (result, ==, tc->expected_result);
-  g_assert_cmpstr (polkit_details_lookup (details, "test_detail"), ==, tc->expected_detail);
 
   g_clear_object (&user_for_subject);
   g_clear_object (&subject);


More information about the hal-commit mailing list