/* * This file is part of Cockpit. * * Copyright (C) 2017 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 . */ "use strict"; import React from "react"; import { Breadcrumb, BreadcrumbItem, Button, Flex, Form, FormGroup, FormSelect, FormSelectOption, TextInput, ActionGroup, Spinner, Card, CardTitle, CardBody, Checkbox, Bullseye, EmptyState, EmptyStateIcon, Title, EmptyStateBody, EmptyStateVariant, Page, PageSection, } from "@patternfly/react-core"; import { ExclamationCircleIcon } from "@patternfly/react-icons"; import { global_danger_color_200 } from "@patternfly/react-tokens"; const json = require('comment-json'); const ini = require('ini'); const cockpit = require('cockpit'); const _ = cockpit.gettext; class GeneralConfig extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); this.setConfig = this.setConfig.bind(this); this.fileReadFailed = this.fileReadFailed.bind(this); this.readConfig = this.readConfig.bind(this); this.file = null; this.config = null; this.state = { config_loaded: false, file_error: false, submitting: false, shell: "", notice: "", latency: "", payload: "", log_input: false, log_output: true, log_window: true, limit_rate: "", limit_burst: "", limit_action: "", file_path: "", syslog_facility: "", syslog_priority: "", journal_augment: "", journal_priority: "", writer: "", }; } handleSubmit(event) { this.setState({ submitting: true }); const config = { shell: this.state.shell, notice: this.state.notice, latency: parseInt(this.state.latency), payload: parseInt(this.state.payload), log: { input: this.state.log_input, output: this.state.log_output, window: this.state.log_window, }, limit: { rate: parseInt(this.state.limit_rate), burst: parseInt(this.state.limit_burst), action: this.state.limit_action, }, file: { path: this.state.file_path, }, syslog: { facility: this.state.syslog_facility, priority: this.state.syslog_priority, }, journal: { priority: this.state.journal_priority, augment: this.state.journal_augment }, writer: this.state.writer }; this.file.replace(config).done(() => { this.setState({ submitting: false }); }) .fail((error) => { console.log(error); }); event.preventDefault(); } setConfig(data) { delete data.configuration; delete data.args; var flattenObject = function(ob) { var toReturn = {}; for (var i in ob) { if (!Object.prototype.hasOwnProperty.call(ob, i)) continue; if ((typeof ob[i]) == 'object') { var flatObject = flattenObject(ob[i]); for (var x in flatObject) { if (!Object.prototype.hasOwnProperty.call(flatObject, x)) continue; toReturn[i + '_' + x] = flatObject[x]; } } else { toReturn[i] = ob[i]; } } return toReturn; }; const state = flattenObject(data); state.config_loaded = true; this.setState(state); } getConfig() { const proc = cockpit.spawn(["tlog-rec-session", "--configuration"]); proc.stream((data) => { this.setConfig(json.parse(data, null, true)); proc.close(); }); proc.fail((fail) => { console.log(fail); this.readConfig(); }); } readConfig() { const parseFunc = function(data) { return json.parse(data, null, true); }; const stringifyFunc = function(data) { return json.stringify(data, null, true); }; // needed for cockpit.file usage const syntax_object = { parse: parseFunc, stringify: stringifyFunc, }; this.file = cockpit.file("/etc/tlog/tlog-rec-session.conf", { syntax: syntax_object, superuser: true, }); } fileReadFailed(reason) { console.log(reason); this.setState({ file_error: reason }); } componentDidMount() { this.getConfig(); this.readConfig(); } render() { const form = (this.state.config_loaded === false && this.state.file_error === false) ? : (this.state.config_loaded === true && this.state.file_error === false) ? (
this.setState({ shell })} /> this.setState({ notice })} /> this.setState({ latency })} /> this.setState({ payload })} /> this.setState({ log_input })} label={_("User's Input")} /> this.setState({ log_output })} label={_("User's Output")} /> this.setState({ log_window })} label={_("Window Resize")} /> this.setState({ limit_rate })} /> this.setState({ limit_burst })} /> this.setState({ limit_action })}> {[ { value: "", label: "" }, { value: "pass", label: _("Pass") }, { value: "delay", label: _("Delay") }, { value: "drop", label: _("Drop") } ].map((option, index) => )} this.setState({ file_path })} /> this.setState({ syslog_facility })} /> this.setState({ syslog_priority })}> {[ { value: "", label: "" }, { value: "info", label: _("Info") }, ].map((option, index) => )} this.setState({ journal_priority })}> {[ { value: "", label: "" }, { value: "info", label: _("Info") }, ].map((option, index) => )} this.setState({ journal_augment })} label={_("Augment")} /> this.setState({ writer })}> {[ { value: "", label: "" }, { value: "journal", label: _("Journal") }, { value: "syslog", label: _("Syslog") }, { value: "file", label: _("File") }, ].map((option, index) => )} {this.state.submitting === true && }
) : ( {_("There is no configuration file of tlog present in your system.")} {_("Please, check the /etc/tlog/tlog-rec-session.conf or if tlog is installed.")} {this.state.file_error} ); return ( General Config {form} ); } } class SssdConfig extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); this.setConfig = this.setConfig.bind(this); this.confSave = this.confSave.bind(this); this.restartSSSD = this.restartSSSD.bind(this); this.file = null; this.state = { scope: "", users: "", exclude_users: "", exclude_groups: "", groups: "", submitting: false, }; } restartSSSD() { let sssd_cmd = ["systemctl", "restart", "sssd"]; cockpit.spawn(sssd_cmd, { superuser: "require" }); this.setState({ submitting: false }); } confSave(obj) { let chmod_cmd = ["chmod", "600", "/etc/sssd/conf.d/sssd-session-recording.conf"]; /* Update nsswitch, this will fail on RHEL8/F34 and lower as 'with-files-domain' feature is not added there */ let authselect_cmd = ["authselect", "select", "sssd", "with-files-domain", "--force"]; this.setState({ submitting: true }); this.file.replace(obj).done(() => { cockpit.spawn(chmod_cmd, { superuser: "require" }) .then(() => { cockpit.spawn(authselect_cmd, { superuser: "require" }) .then(this.restartSSSD) .catch(this.restartSSSD); }); }); } setConfig(data) { if (data === null) { const obj = {}; /* Always enable files domain */ obj.sssd = {}; obj.sssd.enable_files_domain = "true"; obj.sssd.services = "nss"; obj.session_recording = {}; obj.session_recording.scope = "none"; this.confSave(obj); } else { const config = { ...data.session_recording }; this.setState(config); } } componentDidMount() { const syntax_object = { parse: ini.parse, stringify: ini.stringify }; this.file = cockpit.file("/etc/sssd/conf.d/sssd-session-recording.conf", { syntax: syntax_object, superuser: true, }); const promise = this.file.read(); promise.done(() => this.file.watch(this.setConfig)); promise.fail(function(error) { console.log(error); }); } handleSubmit(e) { const obj = {}; obj.sssd = {}; obj.sssd.enable_files_domain = "true"; obj.sssd.services = "nss"; obj.session_recording = {}; obj.session_recording.scope = this.state.scope; switch (this.state.scope) { case "all": obj.session_recording.exclude_users = this.state.exclude_users; obj.session_recording.exclude_groups = this.state.exclude_groups; break; case "none": break; case "some": obj.session_recording.users = this.state.users; obj.session_recording.groups = this.state.groups; break; default: break; } this.confSave(obj); e.preventDefault(); } render() { const form = (
this.setState({ scope })}> {[ { value: "none", label: _("None") }, { value: "some", label: _("Some") }, { value: "all", label: _("All") } ].map((option, index) => )} {this.state.scope === "some" && <> this.setState({ users })} /> this.setState({ groups })} /> } {this.state.scope === "all" && <> this.setState({ exclude_users })} /> this.setState({ exclude_groups })} /> } {this.state.submitting === true && }
); return ( SSSD Config {form} ); } } export function Config () { const goBack = () => { cockpit.location.go("/"); }; return ( {_("Session Recording")} {_("Settings")} }> ); }