LCOV - code coverage report
Current view: top level - pkg/users - group-create-dialog.js Coverage Total Hit
Test: cockpit Lines: 100.0 % 105 105
Test Date: 2026-06-16 14:09:37

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2023 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6           15 : import cockpit from 'cockpit';
       7           15 : import React from 'react';
       8              : 
       9              : import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form/index.js";
      10              : import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput/index.js";
      11              : import { show_modal_dialog, apply_modal_dialog } from "cockpit-components-dialog.jsx";
      12              : import { FormHelper } from "cockpit-components-form-helper";
      13              : 
      14              : import { has_errors, is_valid_char_name } from "./dialog-utils.js";
      15              : 
      16           15 : const _ = cockpit.gettext;
      17              : 
      18            1 : function GroupCreateBody({ state, errors, change }) {
      19            1 :     const {
      20            1 :         name, id,
      21            1 :     } = state;
      22              : 
      23            1 :     return (
      24            1 :         <Form isHorizontal onSubmit={apply_modal_dialog}>
      25            1 :             <FormGroup label={_("Name")}
      26            1 :                        fieldId="groups-create-name">
      27            1 :                 <TextInput id="groups-create-name"
      28            1 :                            validated={(errors?.name) ? "error" : "default"}
      29            1 :                            value={name} onChange={(_event, value) => change("name", value)} />
      30            1 :                 <FormHelper fieldId="groups-create-name" helperTextInvalid={errors?.name} />
      31            1 :             </FormGroup>
      32              : 
      33            1 :             <FormGroup label={_("ID")}
      34            1 :                        hasNoPaddingTop
      35            1 :                        isStack
      36            1 :                        fieldId="groups-create-id">
      37            1 :                 <TextInput id="groups-create-id"
      38            1 :                            validated={(errors?.id) ? "error" : "default"}
      39            1 :                            value={id} onChange={(_event, value) => change("id", value)} />
      40            1 :                 <FormHelper fieldId="groups-create-id" helperTextInvalid={errors?.id} />
      41            1 :             </FormGroup>
      42            1 :         </Form>
      43              :     );
      44            1 : }
      45              : 
      46            1 : function validate_name(name, groups) {
      47            1 :     if (!name)
      48            1 :         return _("No group name specified");
      49              : 
      50            1 :     for (let i = 0; i < name.length; i++) {
      51            1 :         if (!is_valid_char_name(name[i]))
      52            1 :             return _("Group name can only consist of letters, digits, dots, dashes, and underscores");
      53            1 :     }
      54              : 
      55            1 :     for (let k = 0; k < groups.length; k++) {
      56            1 :         if (groups[k].name == name)
      57            1 :             return _("A group with this name already exists");
      58            1 :     }
      59              : 
      60            1 :     return null;
      61            1 : }
      62              : 
      63            1 : function validate_group(id, groups) {
      64            1 :     if (!id)
      65            1 :         return _("No ID specified");
      66              : 
      67            1 :     const id_num = parseInt(id);
      68            1 :     if (id_num.toString() !== id || id_num < 0)
      69            1 :         return _("Group ID must be positive integer");
      70              : 
      71            1 :     return null;
      72            1 : }
      73              : 
      74            1 : export function group_create_dialog(groups, setGroupsCardExpanded, min_gid, max_gid) {
      75            1 :     let dlg = null;
      76            1 :     const state = {
      77            1 :         name: "",
      78            1 :         id: "",
      79            1 :     };
      80            1 :     let errors = { };
      81              : 
      82            1 :     const gids = groups
      83            1 :             .filter(g => g.name !== 'nobody')
      84            1 :             .map(group => group.gid);
      85              : 
      86            1 :     change("id", (Math.max(min_gid, Math.max(...gids.filter(id => id < max_gid)) + 1) + 1).toString());
      87              : 
      88            1 :     function change(field, value) {
      89            1 :         state[field] = value;
      90            1 :         errors = { };
      91              : 
      92            1 :         update();
      93            1 :     }
      94              : 
      95            1 :     function validate(name, id) {
      96            1 :         const errs = { };
      97              : 
      98            1 :         errs.name = validate_name(name, groups);
      99            1 :         errs.id = validate_group(id, groups);
     100            1 :         errors = errs;
     101              : 
     102            1 :         return !has_errors(errs);
     103            1 :     }
     104              : 
     105            1 :     function create(name, id) {
     106            1 :         const valid = validate(name, id);
     107            1 :         if (valid) {
     108            1 :             const group_add_cmd = ["groupadd", name, "-g", id];
     109              : 
     110            1 :             return cockpit.spawn(group_add_cmd, { superuser: "require", err: "message" });
     111            1 :         } else {
     112            1 :             update();
     113            1 :             return Promise.reject();
     114            1 :         }
     115            1 :     }
     116              : 
     117            1 :     function update() {
     118            1 :         const props = {
     119            1 :             id: "groups-create-dialog",
     120            1 :             title: _("Create new group"),
     121            1 :             body: <GroupCreateBody state={state} errors={errors} change={change} />
     122            1 :         };
     123              : 
     124            1 :         const footer = {
     125            1 :             actions: [
     126            1 :                 {
     127            1 :                     caption: _("Create"),
     128            1 :                     style: "primary",
     129            1 :                     clicked: () => create(state.name, state.id).then(() => setGroupsCardExpanded(true))
     130            1 :                 }
     131            1 :             ]
     132            1 :         };
     133              : 
     134            1 :         if (!dlg)
     135            1 :             dlg = show_modal_dialog(props, footer);
     136            1 :         else {
     137            1 :             dlg.setProps(props);
     138            1 :             dlg.setFooterProps(footer);
     139            1 :         }
     140            1 :     }
     141              : 
     142            1 :     update();
     143            1 : }
        

Generated by: LCOV version 2.0-1