From 57906540fef30b2b8801e4abbf38ca12d7307f9f Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 8 Dec 2024 08:57:45 -0600 Subject: [PATCH] Add:Server setting to allow iframe & update UI to differentiate web client settings #3684 --- client/pages/config/index.vue | 47 ++++++++++++++--------- client/store/index.js | 11 +++--- client/strings/en-us.json | 2 + server/Server.js | 3 +- server/controllers/MiscController.js | 5 ++- server/objects/settings/ServerSettings.js | 8 ++++ 6 files changed, 49 insertions(+), 27 deletions(-) diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue index 1f0d61eb..bbb75b93 100644 --- a/client/pages/config/index.vue +++ b/client/pages/config/index.vue @@ -42,11 +42,6 @@ -
- -

{{ $strings.LabelSettingsChromecastSupport }}

-
-

{{ $strings.HeaderSettingsScanner }}

@@ -94,6 +89,20 @@

+ +
+

{{ $strings.HeaderSettingsWebClient }}

+
+ +
+ +

{{ $strings.LabelSettingsChromecastSupport }}

+
+ +
+ +

{{ $strings.LabelSettingsAllowIframe }}

+
@@ -324,21 +333,21 @@ export default { }, updateServerSettings(payload) { this.updatingServerSettings = true - this.$store - .dispatch('updateServerSettings', payload) - .then(() => { - this.updatingServerSettings = false + this.$store.dispatch('updateServerSettings', payload).then((response) => { + this.updatingServerSettings = false - if (payload.language) { - // Updating language after save allows for re-rendering - this.$setLanguageCode(payload.language) - } - }) - .catch((error) => { - console.error('Failed to update server settings', error) - this.updatingServerSettings = false - this.$toast.error(this.$strings.ToastFailedToUpdate) - }) + if (response.error) { + console.error('Failed to update server settins', response.error) + this.$toast.error(response.error) + this.initServerSettings() + return + } + + if (payload.language) { + // Updating language after save allows for re-rendering + this.$setLanguageCode(payload.language) + } + }) }, initServerSettings() { this.newServerSettings = this.serverSettings ? { ...this.serverSettings } : {} diff --git a/client/store/index.js b/client/store/index.js index acd03eb4..2f2201b6 100644 --- a/client/store/index.js +++ b/client/store/index.js @@ -72,16 +72,17 @@ export const actions = { return this.$axios .$patch('/api/settings', updatePayload) .then((result) => { - if (result.success) { + if (result.serverSettings) { commit('setServerSettings', result.serverSettings) - return true - } else { - return false } + return result }) .catch((error) => { console.error('Failed to update server settings', error) - return false + const errorMsg = error.response?.data || 'Unknown error' + return { + error: errorMsg + } }) }, checkForUpdate({ commit }) { diff --git a/client/strings/en-us.json b/client/strings/en-us.json index 75069cd3..805e8f48 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -190,6 +190,7 @@ "HeaderSettingsExperimental": "Experimental Features", "HeaderSettingsGeneral": "General", "HeaderSettingsScanner": "Scanner", + "HeaderSettingsWebClient": "Web Client", "HeaderSleepTimer": "Sleep Timer", "HeaderStatsLargestItems": "Largest Items", "HeaderStatsLongestItems": "Longest Items (hrs)", @@ -542,6 +543,7 @@ "LabelServerYearReview": "Server Year in Review ({0})", "LabelSetEbookAsPrimary": "Set as primary", "LabelSetEbookAsSupplementary": "Set as supplementary", + "LabelSettingsAllowIframe": "Allow embedding in an iframe", "LabelSettingsAudiobooksOnly": "Audiobooks only", "LabelSettingsAudiobooksOnlyHelp": "Enabling this setting will ignore ebook files unless they are inside an audiobook folder in which case they will be set as supplementary ebooks", "LabelSettingsBookshelfViewHelp": "Skeumorphic design with wooden shelves", diff --git a/server/Server.js b/server/Server.js index 79598275..2f1220d8 100644 --- a/server/Server.js +++ b/server/Server.js @@ -53,7 +53,6 @@ class Server { global.RouterBasePath = ROUTER_BASE_PATH global.XAccel = process.env.USE_X_ACCEL global.AllowCors = process.env.ALLOW_CORS === '1' - global.AllowIframe = process.env.ALLOW_IFRAME === '1' global.DisableSsrfRequestFilter = process.env.DISABLE_SSRF_REQUEST_FILTER === '1' if (!fs.pathExistsSync(global.ConfigPath)) { @@ -195,7 +194,7 @@ class Server { const app = express() app.use((req, res, next) => { - if (!global.AllowIframe) { + if (!global.ServerSettings.allowIframe) { // Prevent clickjacking by disallowing iframes res.setHeader('Content-Security-Policy', "frame-ancestors 'self'") } diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index 2a87f2fe..b35619b7 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -126,6 +126,10 @@ class MiscController { if (!isObject(settingsUpdate)) { return res.status(400).send('Invalid settings update object') } + if (settingsUpdate.allowIframe == false && process.env.ALLOW_IFRAME === '1') { + Logger.warn('Cannot disable iframe when ALLOW_IFRAME is enabled in environment') + return res.status(400).send('Cannot disable iframe when ALLOW_IFRAME is enabled in environment') + } const madeUpdates = Database.serverSettings.update(settingsUpdate) if (madeUpdates) { @@ -137,7 +141,6 @@ class MiscController { } } return res.json({ - success: true, serverSettings: Database.serverSettings.toJSONForBrowser() }) } diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js index ff28027f..29913e44 100644 --- a/server/objects/settings/ServerSettings.js +++ b/server/objects/settings/ServerSettings.js @@ -24,6 +24,7 @@ class ServerSettings { // Security/Rate limits this.rateLimitLoginRequests = 10 this.rateLimitLoginWindow = 10 * 60 * 1000 // 10 Minutes + this.allowIframe = false // Backups this.backupPath = Path.join(global.MetadataPath, 'backups') @@ -99,6 +100,7 @@ class ServerSettings { this.rateLimitLoginRequests = !isNaN(settings.rateLimitLoginRequests) ? Number(settings.rateLimitLoginRequests) : 10 this.rateLimitLoginWindow = !isNaN(settings.rateLimitLoginWindow) ? Number(settings.rateLimitLoginWindow) : 10 * 60 * 1000 // 10 Minutes + this.allowIframe = !!settings.allowIframe this.backupPath = settings.backupPath || Path.join(global.MetadataPath, 'backups') this.backupSchedule = settings.backupSchedule || false @@ -190,6 +192,11 @@ class ServerSettings { Logger.info(`[ServerSettings] Using backup path from environment variable ${process.env.BACKUP_PATH}`) this.backupPath = process.env.BACKUP_PATH } + + if (process.env.ALLOW_IFRAME === '1' && !this.allowIframe) { + Logger.info(`[ServerSettings] Using allowIframe from environment variable`) + this.allowIframe = true + } } toJSON() { @@ -207,6 +214,7 @@ class ServerSettings { metadataFileFormat: this.metadataFileFormat, rateLimitLoginRequests: this.rateLimitLoginRequests, rateLimitLoginWindow: this.rateLimitLoginWindow, + allowIframe: this.allowIframe, backupPath: this.backupPath, backupSchedule: this.backupSchedule, backupsToKeep: this.backupsToKeep,