Skip to content

Commit

Permalink
Merge pull request #5270 from rvykydal/global-password-policies
Browse files Browse the repository at this point in the history
Global password policies
  • Loading branch information
rvykydal authored Oct 23, 2023
2 parents 561cb39 + 008745b commit 4d1a08b
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 19 deletions.
36 changes: 36 additions & 0 deletions ui/webui/src/actions/runtime-actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2023 Red Hat, Inc.
*
* This program 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.
*
* This program 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 This program; If not, see <http://www.gnu.org/licenses/>.
*/

import {
getPasswordPolicies,
} from "../apis/runtime.js";
import { setCriticalErrorAction } from "../actions/miscellaneous-actions.js";

export const getPasswordPoliciesAction = () => {
return async (dispatch) => {
try {
const passwordPolicies = await getPasswordPolicies();

return dispatch({
type: "GET_RUNTIME_PASSWORD_POLICIES",
payload: { passwordPolicies }
});
} catch (error) {
setCriticalErrorAction(error);
}
};
};
28 changes: 28 additions & 0 deletions ui/webui/src/apis/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

import cockpit from "cockpit";

import { getPasswordPoliciesAction } from "../actions/runtime-actions.js";
import { debug } from "../helpers/log.js";

export class RuntimeClient {
constructor (address) {
if (RuntimeClient.instance && (!address || RuntimeClient.instance.address === address)) {
Expand Down Expand Up @@ -78,3 +81,28 @@ export const getPasswordPolicies = () => {
.then(res => res[0].v)
);
};

export const startEventMonitorRuntime = ({ dispatch }) => {
return new RuntimeClient().client.subscribe(
{ },
(path, iface, signal, args) => {
switch (signal) {
case "PropertiesChanged":
if (args[0] === "org.fedoraproject.Anaconda.Modules.Runtime.UserInterface" && Object.hasOwn(args[1], "PasswordPolicies")) {
dispatch(getPasswordPoliciesAction());
} else {
debug(`Unhandled signal on ${path}: ${iface}.${signal}`, JSON.stringify(args));
}
break;
default:
debug(`Unhandled signal on ${path}: ${iface}.${signal}`, JSON.stringify(args));
}
}
);
};

export const initDataRuntime = ({ dispatch }) => {
return Promise.all([
dispatch(getPasswordPoliciesAction())
]);
};
8 changes: 6 additions & 2 deletions ui/webui/src/components/AnacondaWizard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { SystemTypeContext, OsReleaseContext } from "./Common.jsx";
const _ = cockpit.gettext;
const N_ = cockpit.noop;

export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCritFail, title, conf }) => {
export const AnacondaWizard = ({ dispatch, storageData, localizationData, runtimeData, onCritFail, title, conf }) => {
const [isFormDisabled, setIsFormDisabled] = useState(false);
const [isFormValid, setIsFormValid] = useState(false);
const [requiredMountPoints, setRequiredMountPoints] = useState();
Expand Down Expand Up @@ -133,7 +133,11 @@ export const AnacondaWizard = ({ dispatch, storageData, localizationData, onCrit
...getMountPointMappingProps({ storageScenarioId })
}, {
component: DiskEncryption,
data: { storageEncryption, setStorageEncryption },
data: {
storageEncryption,
setStorageEncryption,
passwordPolicies: runtimeData.passwordPolicies,
},
...getDiskEncryptionProps({ storageScenarioId })
}]
},
Expand Down
5 changes: 4 additions & 1 deletion ui/webui/src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { BossClient } from "../apis/boss.js";
import { LocalizationClient, initDataLocalization, startEventMonitorLocalization } from "../apis/localization.js";
import { StorageClient, initDataStorage, startEventMonitorStorage } from "../apis/storage.js";
import { PayloadsClient } from "../apis/payloads";
import { RuntimeClient } from "../apis/runtime";
import { RuntimeClient, initDataRuntime, startEventMonitorRuntime } from "../apis/runtime";
import { NetworkClient, initDataNetwork, startEventMonitorNetwork } from "../apis/network.js";

import { setCriticalErrorAction } from "../actions/miscellaneous-actions.js";
Expand Down Expand Up @@ -87,12 +87,14 @@ export const Application = () => {
initDataStorage({ dispatch }),
initDataLocalization({ dispatch }),
initDataNetwork({ dispatch }),
initDataRuntime({ dispatch }),
])
.then(() => {
setStoreInitialized(true);
startEventMonitorStorage({ dispatch });
startEventMonitorLocalization({ dispatch });
startEventMonitorNetwork({ dispatch });
startEventMonitorRuntime({ dispatch });
}, onCritFail({ context: N_("Reading information about the computer failed.") }));
});

Expand Down Expand Up @@ -147,6 +149,7 @@ export const Application = () => {
title={title}
storageData={state.storage}
localizationData={state.localization}
runtimeData={state.runtime}
dispatch={dispatch}
conf={conf}
osRelease={osRelease}
Expand Down
25 changes: 10 additions & 15 deletions ui/webui/src/components/storage/DiskEncryption.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ import CheckCircleIcon from "@patternfly/react-icons/dist/esm/icons/check-circle

import "./DiskEncryption.scss";

import { getPasswordPolicies } from "../../apis/runtime.js";

const _ = cockpit.gettext;

/* Calculate the password quality levels based on the password policy
Expand Down Expand Up @@ -113,7 +111,7 @@ const passwordStrengthLabel = (idPrefix, strength, strengthLevels) => {
// TODO create strengthLevels object with methods passed to the component ?
const PasswordFormFields = ({
idPrefix,
luksPolicies,
policy,
password,
passwordLabel,
onChange,
Expand Down Expand Up @@ -172,7 +170,7 @@ const PasswordFormFields = ({
variant={ruleLength}
component="li"
>
{cockpit.format(_("Must be at least $0 characters"), luksPolicies["min-length"].v)}
{cockpit.format(_("Must be at least $0 characters"), policy["min-length"].v)}
</HelperTextItem>
{ruleAscii &&
<HelperTextItem
Expand Down Expand Up @@ -269,32 +267,29 @@ export const DiskEncryption = ({
setIsFormValid,
storageEncryption,
setStorageEncryption,
passwordPolicies,
}) => {
const [password, setPassword] = useState(storageEncryption.password);
const [confirmPassword, setConfirmPassword] = useState(storageEncryption.confirmPassword);
const [passwordStrength, setPasswordStrength] = useState("");
const isEncrypted = storageEncryption.encrypt;
const [luksPolicies, setLuksPolicies] = useState();

useEffect(() => {
getPasswordPolicies().then(policies => setLuksPolicies(policies.luks));
}, []);
const luksPolicy = passwordPolicies.luks;

const ruleConfirmMatches = useMemo(() => {
return getRuleConfirmMatches(password, confirmPassword);
}, [password, confirmPassword]);

const ruleLength = useMemo(() => {
return luksPolicies && getRuleLength(password, luksPolicies["min-length"].v);
}, [password, luksPolicies]);
return luksPolicy && getRuleLength(password, luksPolicy["min-length"].v);
}, [password, luksPolicy]);

const ruleAscii = useMemo(() => {
return password.length > 0 && !/^[\x20-\x7F]*$/.test(password);
}, [password]);

const strengthLevels = useMemo(() => {
return luksPolicies && getStrengthLevels(luksPolicies["min-quality"].v, luksPolicies["is-strict"].v);
}, [luksPolicies]);
return luksPolicy && getStrengthLevels(luksPolicy["min-quality"].v, luksPolicy["is-strict"].v);
}, [luksPolicy]);

const encryptedDevicesCheckbox = content => (
<Checkbox
Expand All @@ -309,7 +304,7 @@ export const DiskEncryption = ({
const passphraseForm = (
<PasswordFormFields
idPrefix={idPrefix}
luksPolicies={luksPolicies}
policy={luksPolicy}
password={password}
passwordLabel={_("Passphrase")}
passwordStrength={passwordStrength}
Expand Down Expand Up @@ -357,7 +352,7 @@ export const DiskEncryption = ({
setStorageEncryption(se => ({ ...se, confirmPassword }));
}, [confirmPassword, setStorageEncryption]);

if (isInProgress || !luksPolicies) {
if (isInProgress) {
return CheckDisksSpinner;
}

Expand Down
17 changes: 16 additions & 1 deletion ui/webui/src/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,18 @@ export const errorInitialState = {
criticalError: null
};

/* Intial state for the runtime store substate */
export const runtimeInitialState = {
connected: null
};

/* Initial state for the global store */
export const initialState = {
localization: localizationInitialState,
storage: storageInitialState,
network: networkInitialState,
error: errorInitialState,
runtime: runtimeInitialState,
};

/* Custom hook to use the reducer with async actions */
Expand All @@ -77,7 +83,8 @@ export const reducer = (state, action) => {
localization: localizationReducer(state.localization, action),
storage: storageReducer(state.storage, action),
network: networkReducer(state.network, action),
error: errorReducer(state.error, action)
error: errorReducer(state.error, action),
runtime: runtimeReducer(state.runtime, action),
});
};

Expand Down Expand Up @@ -120,3 +127,11 @@ const errorReducer = (state = errorInitialState, action) => {
return state;
}
};

export const runtimeReducer = (state = runtimeInitialState, action) => {
if (action.type === "GET_RUNTIME_PASSWORD_POLICIES") {
return { ...state, passwordPolicies: action.payload.passwordPolicies };
} else {
return state;
}
};

0 comments on commit 4d1a08b

Please sign in to comment.