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-06-25 09:20:42

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2020 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6          364 : 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          364 : function Superuser() {
      64          364 :     const proxy = cockpit.dbus(null, { bus: "internal" }).proxy("cockpit.Superuser", "/superuser");
      65          364 :     let reload_on_change = false;
      66              : 
      67          364 :     const compute_allowed = () => {
      68          359 :         if (!proxy.valid || proxy.Current == "init")
      69          364 :             return null;
      70          359 :         return proxy.Current != "none";
      71          364 :     };
      72              : 
      73          358 :     const compute_configured = () => {
      74          358 :         if (proxy.Current == "init")
      75           77 :             return null;
      76           64 :         return (proxy.Bridges?.length ?? 0) > 0;
      77          358 :     };
      78              : 
      79          364 :     const self = cockpit.event_target({
      80          364 :         allowed: compute_allowed(),
      81          364 :         configured: null,
      82          364 :         reload_page_on_change
      83          364 :     });
      84              : 
      85          359 :     function changed(allowed) {
      86          359 :         if (self.allowed != allowed) {
      87           66 :             if (self.allowed != null && reload_on_change) {
      88           66 :                 window.location.reload(true);
      89           66 :             } else {
      90          359 :                 const prev = self.allowed;
      91          359 :                 self.allowed = allowed;
      92          359 :                 self.configured = compute_configured();
      93          359 :                 self.dispatchEvent("changed");
      94          359 :                 if (prev != null)
      95           78 :                     self.dispatchEvent("reconnect");
      96          359 :             }
      97          359 :         }
      98          359 :     }
      99              : 
     100          359 :     proxy.wait(() => {
     101           65 :         if (!proxy.valid) {
     102              :             // Fall back to cockpit.permissions
     103           65 :             const permission = cockpit.permission({ admin: true });
     104            0 :             const update = () => {
     105            0 :                 changed(permission.allowed);
     106            0 :             };
     107           65 :             permission.addEventListener("changed", update);
     108           65 :             update();
     109           65 :         }
     110          359 :     });
     111              : 
     112          359 :     proxy.addEventListener("changed", () => {
     113          359 :         changed(compute_allowed());
     114          359 :     });
     115              : 
     116          189 :     function reload_page_on_change() {
     117          189 :         reload_on_change = true;
     118          189 :     }
     119              : 
     120          364 :     return self;
     121          364 : }
     122              : 
     123          364 : export const superuser = Superuser();
        

Generated by: LCOV version 2.0-1