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 @@
-
-
updateSettingsKey('chromecastEnabled', val)" />
- {{ $strings.LabelSettingsChromecastSupport }}
-
-
{{ $strings.HeaderSettingsScanner }}
@@ -94,6 +89,20 @@
+
+
+
{{ $strings.HeaderSettingsWebClient }}
+
+
+
+
updateSettingsKey('chromecastEnabled', val)" />
+ {{ $strings.LabelSettingsChromecastSupport }}
+
+
+
+
updateSettingsKey('allowIframe', val)" />
+ {{ $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,