mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-02-10 18:05:07 +08:00
[fix] Rotate wopi keys
This commit is contained in:
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,11 +2,6 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Ensure proper spacing with tabs
|
||||
:global(.tabsContainer) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pageWithFixedSave {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user