LCOV - code coverage report
Current view: top level - pkg/lib - superuser.js Coverage Total Hit
Test: cockpit Lines: 94.2 % 52 49
Test Date: 2026-07-17 07:33:01

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2020 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6          362 : import cockpit from "cockpit";
       7              : 
       8              : /* import { superuser } from "superuser";
       9              :  *
      10              :  * The "superuser" object indicates whether or not the current page
      11              :  * can open superuser channels.
      12              :  *
      13              :  * - superuser.allowed
      14              :  *
      15              :  * This is true when the page can open superuser channels, and false
      16              :  * otherwise. This field might be "null" while the page or the Cockpit
      17              :  * session itself is still initializing.
      18              :  *
      19              :  * UI elements that trigger actions that need administrative access
      20              :  * should be hidden when the "allowed" field is false or null.  (If
      21              :  * those elements also show information, such as with checkboxes or
      22              :  * toggle buttons, disable them instead of hiding.)
      23              :  *
      24              :  * UI elements that alert the user that they don't have administrative
      25              :  * access should be shown when the "allowed" field is exactly false,
      26              :  * but not when it is null.
      27              :  *
      28              :  * - superuser.configured
      29              :  *
      30              :  * This is true when the shell has at least one superuser bridge configured,
      31              :  * false if there are no superuser bridges, or null during initialization.
      32              :  *
      33              :  * - superuser.addEventListener("changed", () => ...)
      34              :  *
      35              :  * The event handler is called whenever superuser.allowed has changed.
      36              :  * A page should update its appearance according to superuser.allowed.
      37              :  *
      38              :  * - superuser.addEventListener("reconnect", () => ...)
      39              :  *
      40              :  * The event handler is called whenever channels should be re-opened
      41              :  * that use the "superuser" option.
      42              :  *
      43              :  * The difference between "reconnect" and "connect" is that the
      44              :  * "reconnect" signal does not trigger when superuser.allowed goes
      45              :  * from "null" to its first real value.  You don't need to re-open
      46              :  * channels in this case, and it happens on every page load, so this
      47              :  * is important to avoid.
      48              :  *
      49              :  * - superuser.reload_page_on_change()
      50              :  *
      51              :  * Calling this function instructs the "superuser" object to reload
      52              :  * the page whenever "superuser.allowed" changes. This is a (bad)
      53              :  * alternative to re-initializing the page and intended to be used
      54              :  * only to help with the transition.
      55              :  *
      56              :  * Even if you are using "superuser.reload_page_on_change" to avoid having
      57              :  * to re-initialize your page dynamically, you should still use the
      58              :  * "changed" event to update the page appearance since
      59              :  * "superuser.allowed" might still change a couple of times right
      60              :  * after page reload.
      61              :  */
      62              : 
      63          362 : function Superuser() {
      64          362 :     const proxy = cockpit.dbus(null, { bus: "internal" }).proxy("cockpit.Superuser", "/superuser");
      65          362 :     let reload_on_change = false;
      66              : 
      67          362 :     const compute_allowed = () => {
      68          360 :         if (!proxy.valid || proxy.Current == "init")
      69          362 :             return null;
      70          360 :         return proxy.Current != "none";
      71          362 :     };
      72              : 
      73          360 :     const compute_configured = () => {
      74          360 :         if (proxy.Current == "init")
      75           77 :             return null;
      76           64 :         return (proxy.Bridges?.length ?? 0) > 0;
      77          360 :     };
      78              : 
      79          362 :     const self = cockpit.event_target({
      80          362 :         allowed: compute_allowed(),
      81          362 :         configured: null,
      82          362 :         reload_page_on_change
      83          362 :     });
      84              : 
      85          360 :     function changed(allowed) {
      86          360 :         if (self.allowed != allowed) {
      87           65 :             if (self.allowed != null && reload_on_change) {
      88           65 :                 window.location.reload(true);
      89           65 :             } else {
      90          360 :                 const prev = self.allowed;
      91          360 :                 self.allowed = allowed;
      92          360 :                 self.configured = compute_configured();
      93          360 :                 self.dispatchEvent("changed");
      94          360 :                 if (prev != null)
      95           77 :                     self.dispatchEvent("reconnect");
      96          360 :             }
      97          360 :         }
      98          360 :     }
      99              : 
     100          360 :     proxy.wait(() => {
     101           64 :         if (!proxy.valid) {
     102              :             // Fall back to cockpit.permissions
     103           64 :             const permission = cockpit.permission({ admin: true });
     104            0 :             const update = () => {
     105            0 :                 changed(permission.allowed);
     106            0 :             };
     107           64 :             permission.addEventListener("changed", update);
     108           64 :             update();
     109           64 :         }
     110          360 :     });
     111              : 
     112          360 :     proxy.addEventListener("changed", () => {
     113          360 :         changed(compute_allowed());
     114          360 :     });
     115              : 
     116          188 :     function reload_page_on_change() {
     117          188 :         reload_on_change = true;
     118          188 :     }
     119              : 
     120          362 :     return self;
     121          362 : }
     122              : 
     123          362 : export const superuser = Superuser();
        

Generated by: LCOV version 2.0-1