🏡 index : quine-global-labs/mirror-cockpit-project-cockpit.git

/*
 * Copyright (C) 2023 Red Hat, Inc.
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

import cockpit from 'cockpit';
import React from 'react';

import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form/index.js";
import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput/index.js";
import { show_modal_dialog, apply_modal_dialog } from "cockpit-components-dialog.jsx";
import { FormHelper } from "cockpit-components-form-helper";

import { has_errors, is_valid_char_name } from "./dialog-utils.js";

const _ = cockpit.gettext;

function GroupCreateBody({ state, errors, change }) {
    const {
        name, id,
    } = state;

    return (
        <Form isHorizontal onSubmit={apply_modal_dialog}>
            <FormGroup label={_("Name")}
                       fieldId="groups-create-name">
                <TextInput id="groups-create-name"
                           validated={(errors?.name) ? "error" : "default"}
                           value={name} onChange={(_event, value) => change("name", value)} />
                <FormHelper fieldId="groups-create-name" helperTextInvalid={errors?.name} />
            </FormGroup>

            <FormGroup label={_("ID")}
                       hasNoPaddingTop
                       isStack
                       fieldId="groups-create-id">
                <TextInput id="groups-create-id"
                           validated={(errors?.id) ? "error" : "default"}
                           value={id} onChange={(_event, value) => change("id", value)} />
                <FormHelper fieldId="groups-create-id" helperTextInvalid={errors?.id} />
            </FormGroup>
        </Form>
    );
}

function validate_name(name, groups) {
    if (!name)
        return _("No group name specified");

    for (let i = 0; i < name.length; i++) {
        if (!is_valid_char_name(name[i]))
            return _("Group name can only consist of letters, digits, dots, dashes, and underscores");
    }

    for (let k = 0; k < groups.length; k++) {
        if (groups[k].name === name)
            return _("A group with this name already exists");
    }

    return null;
}

function validate_group(id, groups) {
    if (!id)
        return _("No ID specified");

    const id_num = parseInt(id);
    if (id_num.toString() !== id || id_num < 0)
        return _("Group ID must be positive integer");

    return null;
}

export function group_create_dialog(groups, setGroupsCardExpanded, min_gid, max_gid) {
    let dlg = null;
    const state = {
        name: "",
        id: "",
    };
    let errors = { };

    const gids = groups
            .filter(g => g.name !== 'nobody')
            .map(group => group.gid);

    change("id", (Math.max(min_gid, Math.max(...gids.filter(id => id < max_gid)) + 1) + 1).toString());

    function change(field, value) {
        state[field] = value;
        errors = { };

        update();
    }

    function validate(name, id) {
        const errs = { };

        errs.name = validate_name(name, groups);
        errs.id = validate_group(id, groups);
        errors = errs;

        return !has_errors(errs);
    }

    function create(name, id) {
        const valid = validate(name, id);
        if (valid) {
            const group_add_cmd = ["groupadd", name, "-g", id];

            return cockpit.spawn(group_add_cmd, { superuser: "require", err: "message" });
        } else {
            update();
            return Promise.reject();
        }
    }

    function update() {
        const props = {
            id: "groups-create-dialog",
            title: _("Create new group"),
            body: <GroupCreateBody state={state} errors={errors} change={change} />
        };

        const footer = {
            actions: [
                {
                    caption: _("Create"),
                    style: "primary",
                    clicked: () => create(state.name, state.id).then(() => setGroupsCardExpanded(true))
                }
            ]
        };

        if (!dlg)
            dlg = show_modal_dialog(props, footer);
        else {
            dlg.setProps(props);
            dlg.setFooterProps(footer);
        }
    }

    update();
}