* Copyright (C) 2025 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import cockpit from "cockpit";
import * as python from "python.js";
import {
clearRootPassword,
setCryptedRootPassword,
setIsRootAccountLocked,
setUsers,
} from "../apis/users.js";
import { objectToDbus } from "../apis/helpers.js";
import encryptUserPw from "../scripts/encrypt-user-pw.py";
* Whether the installer UI may change root password / lock state.
*
* Ported from Anaconda's shared UI helper (same control flow as
* `can_modify_root_configuration` in pyanaconda):
* https://github.com/rhinstaller/anaconda/blob/main/pyanaconda/ui/lib/users.py
*
* @param {object} opts
* @param {boolean} opts.automatedInstall Same role as Anaconda `flags.automatedInstall`
* @param {boolean} opts.canChangeRoot Same role as Anaconda `conf.ui.can_change_root`
* @param {boolean} opts.canChangeRootPassword Users module `CanChangeRootPassword`
* @returns {boolean}
*/
export const canModifyRootConfiguration = ({
automatedInstall,
canChangeRoot,
canChangeRootPassword,
}) => {
if (!automatedInstall) {
return true;
}
if (canChangeRoot) {
return true;
}
if (canChangeRootPassword) {
return true;
}
return false;
};
* Whether the Accounts user-creation section may be edited when the Users module
* already lists accounts (e.g. kickstart / interactive defaults).
* Aligns with Anacondaβs use of `conf.ui.can_change_users` for the user spoke.
*
* @param {object} opts
* @param {boolean} opts.canChangeUsers Same role as Anaconda `conf.ui.can_change_users`
* @param {boolean} opts.usersSpecifiedByKickstart Installer already has user entries to show
* @returns {boolean}
*/
export const canModifyUserConfiguration = ({ canChangeUsers, usersSpecifiedByKickstart }) => {
return !usersSpecifiedByKickstart || canChangeUsers;
};
const cryptUserPassword = async (password) => {
const crypted = await python.spawn(encryptUserPw, password, { environ: ["LC_ALL=C.UTF-8"], err: "message" });
return crypted;
};
export const applyAccounts = async (accounts) => {
if ((accounts.users?.length ?? 0) === 0) {
await setUsers([]);
} else if (accounts.canModifyUserConfiguration) {
const password = typeof accounts.password === "string" ? accounts.password : "";
const hasUserPassword = password.length > 0;
const userPassword = hasUserPassword
? await cryptUserPassword(password)
: "";
const first = accounts.users?.[0] ?? {};
const firstUserDbus = firstUserToDbus({
...first,
isCrypted: hasUserPassword,
password: userPassword,
});
const existing = accounts.users ?? [];
const users = existing.length > 0
? [firstUserDbus, ...existing.slice(1).map(u => objectToDbus(u))]
: [firstUserDbus];
await setUsers(users);
}
if (accounts.canModifyRootConfiguration) {
await setIsRootAccountLocked(!accounts.isRootEnabled);
if (accounts.isRootEnabled) {
const cryptedRootPw = await cryptUserPassword(accounts.rootPassword);
await setCryptedRootPassword({ password: cryptedRootPw });
} else {
await clearRootPassword();
}
}
};
const firstUserToDbus = (firstUser) => {
return {
gecos: cockpit.variant("s", firstUser.gecos ?? ""),
groups: cockpit.variant("as", firstUser.groups ?? ["wheel"]),
"is-crypted": cockpit.variant("b", firstUser.isCrypted ?? true),
name: cockpit.variant("s", firstUser.name ?? ""),
password: cockpit.variant("s", firstUser.password ?? ""),
};
};