[fix] Rotate wopi keys

This commit is contained in:
PauI Ostrovckij
2025-09-18 14:12:07 +03:00
parent f7aad5eb2a
commit 470e857e6b
3 changed files with 77 additions and 72 deletions

View File

@ -9,8 +9,7 @@ import PageHeader from '../../components/PageHeader/PageHeader';
import PageDescription from '../../components/PageDescription/PageDescription';
import ToggleSwitch from '../../components/ToggleSwitch/ToggleSwitch';
import Input from '../../components/Input/Input';
import SaveButton from '../../components/SaveButton/SaveButton';
import Tabs from '../../components/Tabs/Tabs';
import Checkbox from '../../components/Checkbox/Checkbox';
import FixedSaveButton from '../../components/FixedSaveButton/FixedSaveButton';
import styles from './WOPISettings.module.scss';
@ -19,25 +18,20 @@ function WOPISettings() {
const config = useSelector(selectConfig);
const {validateField, hasValidationErrors} = useFieldValidation();
// Local state for WOPI enable setting
// Local state for WOPI settings
const [localWopiEnabled, setLocalWopiEnabled] = useState(false);
const [localRotateKeys, setLocalRotateKeys] = useState(false);
const [hasChanges, setHasChanges] = useState(false);
const [activeTab, setActiveTab] = useState('settings');
const hasInitialized = useRef(false);
// Get the actual config values
const configWopiEnabled = getNestedValue(config, 'wopi.enable', false);
const wopiPublicKey = getNestedValue(config, 'wopi.publicKey', '');
// Tabs configuration
const tabs = [
{ key: 'settings', label: 'Settings' },
{ key: 'keys', label: 'Key Management' }
];
const resetToGlobalConfig = () => {
if (config) {
setLocalWopiEnabled(configWopiEnabled);
setLocalRotateKeys(false);
setHasChanges(false);
validateField('wopi.enable', configWopiEnabled);
}
@ -49,88 +43,102 @@ function WOPISettings() {
hasInitialized.current = true;
}
const handleTabChange = (newTab) => {
setActiveTab(newTab);
resetToGlobalConfig();
};
const handleWopiEnabledChange = enabled => {
setLocalWopiEnabled(enabled);
setHasChanges(enabled !== configWopiEnabled);
// If WOPI is disabled, uncheck rotate keys
if (!enabled) {
setLocalRotateKeys(false);
}
setHasChanges(enabled !== configWopiEnabled || localRotateKeys);
// Validate the boolean field
validateField('wopi.enable', enabled);
};
const handleRotateKeysChange = checked => {
setLocalRotateKeys(checked);
setHasChanges(localWopiEnabled !== configWopiEnabled || checked);
};
const handleSave = async () => {
if (!hasChanges) return;
try {
const updatedConfig = mergeNestedObjects([{'wopi.enable': localWopiEnabled}]);
await dispatch(saveConfig(updatedConfig)).unwrap();
const enableChanged = localWopiEnabled !== configWopiEnabled;
const rotateRequested = localRotateKeys;
// If only enable changed, just update config
if (enableChanged && !rotateRequested) {
const updatedConfig = mergeNestedObjects([{'wopi.enable': localWopiEnabled}]);
await dispatch(saveConfig(updatedConfig)).unwrap();
}
// If only rotate requested, just rotate keys
else if (!enableChanged && rotateRequested) {
await dispatch(rotateWopiKeysAction()).unwrap();
}
// If both changed, make two requests
else if (enableChanged && rotateRequested) {
// First update the enable setting
const updatedConfig = mergeNestedObjects([{'wopi.enable': localWopiEnabled}]);
await dispatch(saveConfig(updatedConfig)).unwrap();
// Then rotate keys
await dispatch(rotateWopiKeysAction()).unwrap();
}
setHasChanges(false);
setLocalRotateKeys(false);
} catch (error) {
console.error('Failed to save WOPI settings:', error);
// Revert local state on error
setLocalWopiEnabled(configWopiEnabled);
setLocalRotateKeys(false);
setHasChanges(false);
}
};
const handleRotateKeys = async () => {
await dispatch(rotateWopiKeysAction()).unwrap();
};
const renderSettingsTab = () => (
<div className={styles.settingsSection}>
<ToggleSwitch label='WOPI' checked={localWopiEnabled} onChange={handleWopiEnabledChange} />
</div>
);
const renderKeysTab = () => (
<div className={styles.settingsSection}>
<div className={styles.sectionTitle}>Key Management</div>
<div className={styles.sectionDescription}>
Rotate WOPI encryption keys. Current keys will be moved to "Old" and new keys will be generated.
</div>
<div className={styles.formRow}>
<Input
label="Current Public Key"
value={maskKey(wopiPublicKey)}
disabled
placeholder="No key generated"
width="400px"
style={{fontFamily: 'Courier New, monospace'}}
/>
<SaveButton
onClick={handleRotateKeys}
>
Rotate Keys
</SaveButton>
</div>
</div>
);
return (
<div className={`${styles.wopiSettings} ${activeTab === 'settings' ? styles.pageWithFixedSave : ''}`}>
<div className={`${styles.wopiSettings} ${styles.pageWithFixedSave}`}>
<PageHeader>WOPI Settings</PageHeader>
<PageDescription>Configure WOPI (Web Application Open Platform Interface) support for document editing</PageDescription>
<Tabs
tabs={tabs}
activeTab={activeTab}
onTabChange={handleTabChange}
>
{activeTab === 'settings' && renderSettingsTab()}
{activeTab === 'keys' && renderKeysTab()}
</Tabs>
<div className={styles.settingsSection}>
<ToggleSwitch label='WOPI' checked={localWopiEnabled} onChange={handleWopiEnabledChange} />
</div>
{activeTab === 'settings' && (
<FixedSaveButton onClick={handleSave} disabled={!hasChanges || hasValidationErrors()}>
Save Changes
</FixedSaveButton>
{localWopiEnabled && (
<div className={styles.settingsSection}>
<div className={styles.sectionTitle}>Key Management</div>
<div className={styles.sectionDescription}>
Rotate WOPI encryption keys. Current keys will be moved to "Old" and new keys will be generated.
</div>
<div className={styles.formRow}>
<Input
label="Current Public Key"
value={maskKey(wopiPublicKey)}
disabled
placeholder="No key generated"
width="400px"
style={{fontFamily: 'Courier New, monospace'}}
/>
</div>
<div className={styles.formRow}>
<Checkbox
label="Rotate Keys"
checked={localRotateKeys}
onChange={handleRotateKeysChange}
disabled={!localWopiEnabled}
description="Generate new encryption keys. Current keys will be moved to 'Old'."
/>
</div>
</div>
)}
<FixedSaveButton onClick={handleSave} disabled={!hasChanges || hasValidationErrors()}>
Save Changes
</FixedSaveButton>
</div>
);
}

View File

@ -2,11 +2,6 @@
padding: 0;
}
// Ensure proper spacing with tabs
:global(.tabsContainer) {
margin-bottom: 0;
}
.pageWithFixedSave {
padding-bottom: 80px;
}

View File

@ -161,12 +161,14 @@ router.post('/rotate-keys', validateJWT, express.json(), async (req, res) => {
const newWopiConfig = generateWopiKeys();
const hasEmptyKeys = !wopiConfig.publicKey && !wopiConfig.modulus && !wopiConfig.privateKey;
const configUpdate = {
wopi: {
publicKeyOld: wopiConfig.publicKey || '',
modulusOld: wopiConfig.modulus || '',
exponentOld: wopiConfig.exponent || '',
privateKeyOld: wopiConfig.privateKey || '',
publicKeyOld: hasEmptyKeys ? newWopiConfig.publicKey : wopiConfig.publicKey,
modulusOld: hasEmptyKeys ? newWopiConfig.modulus : wopiConfig.modulus,
exponentOld: hasEmptyKeys ? newWopiConfig.exponent : wopiConfig.exponent,
privateKeyOld: hasEmptyKeys ? newWopiConfig.privateKey : wopiConfig.privateKey,
publicKey: newWopiConfig.publicKey,
modulus: newWopiConfig.modulus,
exponent: newWopiConfig.exponent,