/*
* This file is part of Cockpit.
*
* Copyright (C) 2021 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see .
*/
import React, { useState } from 'react';
import { Alert, AlertActionCloseButton, AlertActionLink } from "@patternfly/react-core/dist/esm/components/Alert/index.js";
import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
import { Select, SelectOption } from "@patternfly/react-core/dist/esm/deprecated/components/Select/index.js";
import { Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from "@patternfly/react-core/dist/esm/components/Toolbar/index.js";
import { PageSection } from "@patternfly/react-core/dist/esm/components/Page/index.js";
import cockpit from 'cockpit';
import './cockpit-components-firewalld-request.scss';
const _ = cockpit.gettext;
const firewalld = cockpit.dbus('org.fedoraproject.FirewallD1', { superuser: "try" });
function debug() {
if (window.debugging == "all" || window.debugging?.includes("firewall"))
console.debug.apply(console, arguments);
}
/* React component for an info alert to enable some new service in firewalld.
* Use this when enabling some network-facing service. The alert will only be shown
* if firewalld is running, has at least one active zone, and the service is not enabled
* in any zone yet. It will allow the user to enable the service in any active zone,
* or go to the firewall page for more fine-grained configuration.
*
* Properties:
* - service (string, required): firewalld service name
* - title (string, required): Human readable/translated alert title
* - pageSection (bool, optional, default false): Render the alert inside a
*/
export const FirewalldRequest = ({ service, title, pageSection }) => {
const [zones, setZones] = useState(null);
const [selectedZone, setSelectedZone] = useState(null);
const [zoneSelectorOpened, setZoneSelectorOpened] = useState(false);
const [enabledAnywhere, setEnabledAnywhere] = useState(null);
const [enableError, setEnableError] = useState(null);
debug("FirewalldRequest", service, "zones", JSON.stringify(zones), "selected zone", selectedZone, "enabledAnywhere", enabledAnywhere);
if (!service)
return null;
// query zones on component initialization
if (zones === null) {
firewalld.call("/org/fedoraproject/FirewallD1", "org.fedoraproject.FirewallD1.zone", "getActiveZones")
.then(([info]) => {
const names = Object.keys(info);
Promise.all(names.map(name => firewalld.call("/org/fedoraproject/FirewallD1", "org.fedoraproject.FirewallD1.zone", "getZoneSettings2", [name])))
.then(zoneInfos => {
setEnabledAnywhere(!!zoneInfos.find(zoneInfo => ((zoneInfo[0].services || {}).v || []).indexOf(service) >= 0));
setZones(names);
})
.catch(ex => {
console.warn("FirewalldRequest: getZoneSettings failed:", JSON.stringify(ex));
setZones([]);
});
firewalld.call("/org/fedoraproject/FirewallD1", "org.fedoraproject.FirewallD1", "getDefaultZone")
.then(([zone]) => setSelectedZone(zone))
.catch(ex => console.warn("FirewalldRequest: getDefaultZone failed:", JSON.stringify(ex)));
})
.catch(ex => {
// firewalld not running
debug("FirewalldRequest: getActiveZones failed, considering firewall inactive:", JSON.stringify(ex));
setZones([]);
});
}
const onAddService = () => {
firewalld.call("/org/fedoraproject/FirewallD1", "org.fedoraproject.FirewallD1.zone", "addService",
[selectedZone, service, 0])
// permanent config
.then(() => firewalld.call("/org/fedoraproject/FirewallD1/config",
"org.fedoraproject.FirewallD1.config",
"getZoneByName", [selectedZone]))
.then(([path]) => firewalld.call(path, "org.fedoraproject.FirewallD1.config.zone", "addService", [service]))
// all successful, hide alert
.then(() => setEnabledAnywhere(true))
.catch(ex => {
// may already be enabled in permanent config, that's ok
if (ex.message && ex.message.indexOf("ALREADY_ENABLED") >= 0) {
setEnabledAnywhere(true);
return;
}
setEnableError(ex.toString());
setEnabledAnywhere(true);
console.error("Failed to enable", service, "in firewalld:", JSON.stringify(ex));
});
};
let alert;
if (enableError) {
alert = (
setEnableError(null) } /> }
actionLinks={
cockpit.jump("/network/firewall")}>
{ _("Visit firewall") }
}>
{enableError}
);
// don't show anything if firewalld is not active, or service is already enabled somewhere
} else if (!zones || zones.length === 0 || !selectedZone || enabledAnywhere) {
return null;
} else {
alert = (
{ _("Zone") }
);
}
if (pageSection)
return {alert};
else
return alert;
};