mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Add authentication page in config, add /auth-settings GET endpoint, remove authOpenIDCallbackURL server setting
This commit is contained in:
parent
7ba10db7d4
commit
e282142d3f
@ -104,6 +104,11 @@ export default {
|
|||||||
id: 'config-rss-feeds',
|
id: 'config-rss-feeds',
|
||||||
title: this.$strings.HeaderRSSFeeds,
|
title: this.$strings.HeaderRSSFeeds,
|
||||||
path: '/config/rss-feeds'
|
path: '/config/rss-feeds'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'config-authentication',
|
||||||
|
title: this.$strings.HeaderAuthentication,
|
||||||
|
path: '/config/authentication'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ export default {
|
|||||||
else if (pageName === 'item-metadata-utils') return this.$strings.HeaderItemMetadataUtils
|
else if (pageName === 'item-metadata-utils') return this.$strings.HeaderItemMetadataUtils
|
||||||
else if (pageName === 'rss-feeds') return this.$strings.HeaderRSSFeeds
|
else if (pageName === 'rss-feeds') return this.$strings.HeaderRSSFeeds
|
||||||
else if (pageName === 'email') return this.$strings.HeaderEmail
|
else if (pageName === 'email') return this.$strings.HeaderEmail
|
||||||
|
else if (pageName === 'authentication') return this.$strings.HeaderAuthentication
|
||||||
}
|
}
|
||||||
return this.$strings.HeaderSettings
|
return this.$strings.HeaderSettings
|
||||||
}
|
}
|
||||||
|
138
client/pages/config/authentication.vue
Normal file
138
client/pages/config/authentication.vue
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<app-settings-content :header-text="$strings.HeaderAuthentication">
|
||||||
|
<div class="w-full border border-white/10 rounded-xl p-4 my-4 bg-primary/25">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<ui-checkbox v-model="enableLocalAuth" checkbox-bg="bg" />
|
||||||
|
<p class="text-lg pl-4">Password Authentication</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full border border-white/10 rounded-xl p-4 my-4 bg-primary/25">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<ui-checkbox v-model="enableOpenIDAuth" checkbox-bg="bg" />
|
||||||
|
<p class="text-lg pl-4">OpenID Connect Authentication</p>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
<transition name="slide">
|
||||||
|
<div v-if="enableOpenIDAuth" class="flex flex-wrap pt-4">
|
||||||
|
<ui-text-input-with-label ref="issuerUrl" v-model="newAuthSettings.authOpenIDIssuerURL" :disabled="savingSettings" :label="'Issuer URL'" class="mb-2" />
|
||||||
|
|
||||||
|
<ui-text-input-with-label ref="authorizationUrl" v-model="newAuthSettings.authOpenIDAuthorizationURL" :disabled="savingSettings" :label="'Authorize URL'" class="mb-2" />
|
||||||
|
|
||||||
|
<ui-text-input-with-label ref="tokenUrl" v-model="newAuthSettings.authOpenIDTokenURL" :disabled="savingSettings" :label="'Token URL'" class="mb-2" />
|
||||||
|
|
||||||
|
<ui-text-input-with-label ref="userInfoUrl" v-model="newAuthSettings.authOpenIDUserInfoURL" :disabled="savingSettings" :label="'Userinfo URL'" class="mb-2" />
|
||||||
|
|
||||||
|
<ui-text-input-with-label ref="openidClientId" v-model="newAuthSettings.authOpenIDClientID" :disabled="savingSettings" :label="'Client ID'" class="mb-2" />
|
||||||
|
|
||||||
|
<ui-text-input-with-label ref="openidClientSecret" v-model="newAuthSettings.authOpenIDClientSecret" :disabled="savingSettings" :label="'Client Secret'" class="mb-2" />
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex items-center justify-end p-4">
|
||||||
|
<ui-btn color="success" :padding-x="8" small class="text-base" :loading="savingSettings" @click="saveSettings">{{ $strings.ButtonSave }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
</app-settings-content>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
async asyncData({ store, redirect, app }) {
|
||||||
|
if (!store.getters['user/getIsAdminOrUp']) {
|
||||||
|
redirect('/')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const authSettings = await app.$axios.$get('/api/auth-settings').catch((error) => {
|
||||||
|
console.error('Failed', error)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
if (!authSettings) {
|
||||||
|
redirect('/config')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
authSettings
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
enableLocalAuth: false,
|
||||||
|
enableOpenIDAuth: false,
|
||||||
|
savingSettings: false,
|
||||||
|
newAuthSettings: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
authMethods() {
|
||||||
|
return this.authSettings.authActiveAuthMethods || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
validateOpenID() {
|
||||||
|
let isValid = true
|
||||||
|
if (!this.newAuthSettings.authOpenIDIssuerURL) {
|
||||||
|
this.$toast.error('Issuer URL required')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
if (!this.newAuthSettings.authOpenIDAuthorizationURL) {
|
||||||
|
this.$toast.error('Authorize URL required')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
if (!this.newAuthSettings.authOpenIDTokenURL) {
|
||||||
|
this.$toast.error('Token URL required')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
if (!this.newAuthSettings.authOpenIDUserInfoURL) {
|
||||||
|
this.$toast.error('Userinfo URL required')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
if (!this.newAuthSettings.authOpenIDClientID) {
|
||||||
|
this.$toast.error('Client ID required')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
if (!this.newAuthSettings.authOpenIDClientSecret) {
|
||||||
|
this.$toast.error('Client Secret required')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
return isValid
|
||||||
|
},
|
||||||
|
async saveSettings() {
|
||||||
|
if (!this.enableLocalAuth && !this.enableOpenIDAuth) {
|
||||||
|
this.$toast.error('Must have at least one authentication method enabled')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.enableOpenIDAuth && !this.validateOpenID()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newAuthSettings.authActiveAuthMethods = []
|
||||||
|
if (this.enableLocalAuth) this.newAuthSettings.authActiveAuthMethods.push('local')
|
||||||
|
if (this.enableOpenIDAuth) this.newAuthSettings.authActiveAuthMethods.push('openid')
|
||||||
|
|
||||||
|
this.savingSettings = true
|
||||||
|
const success = await this.$store.dispatch('updateServerSettings', this.newAuthSettings)
|
||||||
|
this.savingSettings = false
|
||||||
|
if (success) {
|
||||||
|
this.$toast.success('Server settings updated')
|
||||||
|
} else {
|
||||||
|
this.$toast.error('Failed to update server settings')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.newAuthSettings = {
|
||||||
|
...this.authSettings
|
||||||
|
}
|
||||||
|
this.enableLocalAuth = this.authMethods.includes('local')
|
||||||
|
this.enableOpenIDAuth = this.authMethods.includes('openid')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -66,7 +66,7 @@ export const getters = {
|
|||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
updateServerSettings({ commit }, payload) {
|
updateServerSettings({ commit }, payload) {
|
||||||
var updatePayload = {
|
const updatePayload = {
|
||||||
...payload
|
...payload
|
||||||
}
|
}
|
||||||
return this.$axios.$patch('/api/settings', updatePayload).then((result) => {
|
return this.$axios.$patch('/api/settings', updatePayload).then((result) => {
|
||||||
|
@ -88,6 +88,7 @@
|
|||||||
"HeaderAppriseNotificationSettings": "Apprise Notification Settings",
|
"HeaderAppriseNotificationSettings": "Apprise Notification Settings",
|
||||||
"HeaderAudiobookTools": "Audiobook File Management Tools",
|
"HeaderAudiobookTools": "Audiobook File Management Tools",
|
||||||
"HeaderAudioTracks": "Audio Tracks",
|
"HeaderAudioTracks": "Audio Tracks",
|
||||||
|
"HeaderAuthentication": "Authentication",
|
||||||
"HeaderBackups": "Backups",
|
"HeaderBackups": "Backups",
|
||||||
"HeaderChangePassword": "Change Password",
|
"HeaderChangePassword": "Change Password",
|
||||||
"HeaderChapters": "Chapters",
|
"HeaderChapters": "Chapters",
|
||||||
|
@ -57,11 +57,10 @@ class Auth {
|
|||||||
userInfoURL: global.ServerSettings.authOpenIDUserInfoURL,
|
userInfoURL: global.ServerSettings.authOpenIDUserInfoURL,
|
||||||
clientID: global.ServerSettings.authOpenIDClientID,
|
clientID: global.ServerSettings.authOpenIDClientID,
|
||||||
clientSecret: global.ServerSettings.authOpenIDClientSecret,
|
clientSecret: global.ServerSettings.authOpenIDClientSecret,
|
||||||
callbackURL: global.ServerSettings.authOpenIDCallbackURL,
|
callbackURL: '/auth/openid/callback',
|
||||||
scope: ["openid", "email", "profile"],
|
scope: ["openid", "email", "profile"],
|
||||||
skipUserProfile: false
|
skipUserProfile: false
|
||||||
},
|
}, async (issuer, profile, done) => {
|
||||||
(async function (issuer, profile, done) {
|
|
||||||
// TODO: do we want to create the users which does not exist?
|
// TODO: do we want to create the users which does not exist?
|
||||||
|
|
||||||
const user = await Database.userModel.getUserByUsername(profile.username)
|
const user = await Database.userModel.getUserByUsername(profile.username)
|
||||||
@ -74,7 +73,7 @@ class Auth {
|
|||||||
|
|
||||||
// permit login
|
// permit login
|
||||||
return done(null, user)
|
return done(null, user)
|
||||||
}).bind(this)))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the JwtStrategy (always) -> for bearer token auth
|
// Load the JwtStrategy (always) -> for bearer token auth
|
||||||
@ -111,14 +110,13 @@ class Auth {
|
|||||||
* @param {import('express').Response} res
|
* @param {import('express').Response} res
|
||||||
*/
|
*/
|
||||||
paramsToCookies(req, res) {
|
paramsToCookies(req, res) {
|
||||||
if (req.query.isRest && req.query.isRest.toLowerCase() == "true") {
|
if (req.query.isRest?.toLowerCase() == "true") {
|
||||||
// store the isRest flag to the is_rest cookie
|
// store the isRest flag to the is_rest cookie
|
||||||
res.cookie('is_rest', req.query.isRest.toLowerCase(), {
|
res.cookie('is_rest', req.query.isRest.toLowerCase(), {
|
||||||
maxAge: 120000, // 2 min
|
maxAge: 120000, // 2 min
|
||||||
httpOnly: true
|
httpOnly: true
|
||||||
})
|
})
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// no isRest-flag set -> set is_rest cookie to false
|
// no isRest-flag set -> set is_rest cookie to false
|
||||||
res.cookie('is_rest', "false", {
|
res.cookie('is_rest', "false", {
|
||||||
maxAge: 120000, // 2 min
|
maxAge: 120000, // 2 min
|
||||||
@ -126,7 +124,7 @@ class Auth {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// check if we are missing a callback parameter - we need one if isRest=false
|
// check if we are missing a callback parameter - we need one if isRest=false
|
||||||
if (!req.query.callback || req.query.callback === "") {
|
if (!req.query.callback) {
|
||||||
res.status(400).send({
|
res.status(400).send({
|
||||||
message: 'No callback parameter'
|
message: 'No callback parameter'
|
||||||
})
|
})
|
||||||
@ -151,19 +149,17 @@ class Auth {
|
|||||||
// get userLogin json (information about the user, server and the session)
|
// get userLogin json (information about the user, server and the session)
|
||||||
const data_json = await this.getUserLoginResponsePayload(req.user)
|
const data_json = await this.getUserLoginResponsePayload(req.user)
|
||||||
|
|
||||||
if (req.cookies.is_rest && req.cookies.is_rest === "true") {
|
if (req.cookies.is_rest === 'true') {
|
||||||
// REST request - send data
|
// REST request - send data
|
||||||
res.json(data_json)
|
res.json(data_json)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// UI request -> check if we have a callback url
|
// UI request -> check if we have a callback url
|
||||||
// TODO: do we want to somehow limit the values for auth_cb?
|
// TODO: do we want to somehow limit the values for auth_cb?
|
||||||
if (req.cookies.auth_cb && req.cookies.auth_cb.startsWith("http")) {
|
if (req.cookies.auth_cb?.startsWith('http')) {
|
||||||
// UI request -> redirect to auth_cb url and send the jwt token as parameter
|
// UI request -> redirect to auth_cb url and send the jwt token as parameter
|
||||||
res.redirect(302, `${req.cookies.auth_cb}?setToken=${data_json.user.token}`)
|
res.redirect(302, `${req.cookies.auth_cb}?setToken=${data_json.user.token}`)
|
||||||
}
|
} else {
|
||||||
else {
|
res.status(400).send('No callback or already expired')
|
||||||
res.status(400).send("No callback or already expired")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,7 +201,7 @@ class Auth {
|
|||||||
|
|
||||||
// openid strategy callback route (this receives the token from the configured openid login provider)
|
// openid strategy callback route (this receives the token from the configured openid login provider)
|
||||||
router.get('/auth/openid/callback',
|
router.get('/auth/openid/callback',
|
||||||
passport.authenticate('openidconnect'),
|
passport.authenticate('openidconnect', { failureRedirect: '/login', failureMessage: true }),
|
||||||
// on a successfull login: read the cookies and react like the client requested (callback or json)
|
// on a successfull login: read the cookies and react like the client requested (callback or json)
|
||||||
this.handleLoginSuccessBasedOnCookie.bind(this)
|
this.handleLoginSuccessBasedOnCookie.bind(this)
|
||||||
)
|
)
|
||||||
|
@ -163,8 +163,6 @@ class Server {
|
|||||||
|
|
||||||
this.server = http.createServer(app)
|
this.server = http.createServer(app)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
router.use(fileUpload({
|
router.use(fileUpload({
|
||||||
defCharset: 'utf8',
|
defCharset: 'utf8',
|
||||||
defParamCharset: 'utf8',
|
defParamCharset: 'utf8',
|
||||||
|
@ -117,8 +117,9 @@ class MiscController {
|
|||||||
/**
|
/**
|
||||||
* PATCH: /api/settings
|
* PATCH: /api/settings
|
||||||
* Update server settings
|
* Update server settings
|
||||||
* @param {*} req
|
*
|
||||||
* @param {*} res
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
*/
|
*/
|
||||||
async updateServerSettings(req, res) {
|
async updateServerSettings(req, res) {
|
||||||
if (!req.user.isAdminOrUp) {
|
if (!req.user.isAdminOrUp) {
|
||||||
@ -246,8 +247,8 @@ class MiscController {
|
|||||||
* POST: /api/authorize
|
* POST: /api/authorize
|
||||||
* Used to authorize an API token
|
* Used to authorize an API token
|
||||||
*
|
*
|
||||||
* @param {*} req
|
* @param {import('express').Request} req
|
||||||
* @param {*} res
|
* @param {import('express').Response} res
|
||||||
*/
|
*/
|
||||||
async authorize(req, res) {
|
async authorize(req, res) {
|
||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
@ -539,5 +540,19 @@ class MiscController {
|
|||||||
res.status(400).send(error.message)
|
res.status(400).send(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET: api/auth-settings (admin only)
|
||||||
|
*
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
*/
|
||||||
|
getAuthSettings(req, res) {
|
||||||
|
if (!req.user.isAdminOrUp) {
|
||||||
|
Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to get auth settings`)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
return res.json(Database.serverSettings.authenticationSettings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
module.exports = new MiscController()
|
module.exports = new MiscController()
|
@ -64,14 +64,13 @@ class ServerSettings {
|
|||||||
this.authGoogleOauth20ClientSecret = ''
|
this.authGoogleOauth20ClientSecret = ''
|
||||||
this.authGoogleOauth20CallbackURL = ''
|
this.authGoogleOauth20CallbackURL = ''
|
||||||
|
|
||||||
// generic-oauth20 settings
|
// openid settings
|
||||||
this.authOpenIDIssuerURL = ''
|
this.authOpenIDIssuerURL = ''
|
||||||
this.authOpenIDAuthorizationURL = ''
|
this.authOpenIDAuthorizationURL = ''
|
||||||
this.authOpenIDTokenURL = ''
|
this.authOpenIDTokenURL = ''
|
||||||
this.authOpenIDUserInfoURL = ''
|
this.authOpenIDUserInfoURL = ''
|
||||||
this.authOpenIDClientID = ''
|
this.authOpenIDClientID = ''
|
||||||
this.authOpenIDClientSecret = ''
|
this.authOpenIDClientSecret = ''
|
||||||
this.authOpenIDCallbackURL = ''
|
|
||||||
|
|
||||||
if (settings) {
|
if (settings) {
|
||||||
this.construct(settings)
|
this.construct(settings)
|
||||||
@ -126,7 +125,6 @@ class ServerSettings {
|
|||||||
this.authOpenIDUserInfoURL = settings.authOpenIDUserInfoURL || ''
|
this.authOpenIDUserInfoURL = settings.authOpenIDUserInfoURL || ''
|
||||||
this.authOpenIDClientID = settings.authOpenIDClientID || ''
|
this.authOpenIDClientID = settings.authOpenIDClientID || ''
|
||||||
this.authOpenIDClientSecret = settings.authOpenIDClientSecret || ''
|
this.authOpenIDClientSecret = settings.authOpenIDClientSecret || ''
|
||||||
this.authOpenIDCallbackURL = settings.authOpenIDCallbackURL || ''
|
|
||||||
|
|
||||||
if (!Array.isArray(this.authActiveAuthMethods)) {
|
if (!Array.isArray(this.authActiveAuthMethods)) {
|
||||||
this.authActiveAuthMethods = ['local']
|
this.authActiveAuthMethods = ['local']
|
||||||
@ -144,16 +142,15 @@ class ServerSettings {
|
|||||||
|
|
||||||
// remove uninitialized methods
|
// remove uninitialized methods
|
||||||
// OpenID
|
// OpenID
|
||||||
if (this.authActiveAuthMethods.includes('generic-oauth20') && (
|
if (this.authActiveAuthMethods.includes('openid') && (
|
||||||
this.authOpenIDIssuerURL === '' ||
|
this.authOpenIDIssuerURL === '' ||
|
||||||
this.authOpenIDAuthorizationURL === '' ||
|
this.authOpenIDAuthorizationURL === '' ||
|
||||||
this.authOpenIDTokenURL === '' ||
|
this.authOpenIDTokenURL === '' ||
|
||||||
this.authOpenIDUserInfoURL === '' ||
|
this.authOpenIDUserInfoURL === '' ||
|
||||||
this.authOpenIDClientID === '' ||
|
this.authOpenIDClientID === '' ||
|
||||||
this.authOpenIDClientSecret === '' ||
|
this.authOpenIDClientSecret === ''
|
||||||
this.authOpenIDCallbackURL === ''
|
|
||||||
)) {
|
)) {
|
||||||
this.authActiveAuthMethods.splice(this.authActiveAuthMethods.indexOf('generic-oauth20', 0), 1)
|
this.authActiveAuthMethods.splice(this.authActiveAuthMethods.indexOf('openid', 0), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback to local
|
// fallback to local
|
||||||
@ -228,8 +225,7 @@ class ServerSettings {
|
|||||||
authOpenIDTokenURL: this.authOpenIDTokenURL,
|
authOpenIDTokenURL: this.authOpenIDTokenURL,
|
||||||
authOpenIDUserInfoURL: this.authOpenIDUserInfoURL,
|
authOpenIDUserInfoURL: this.authOpenIDUserInfoURL,
|
||||||
authOpenIDClientID: this.authOpenIDClientID, // Do not return to client
|
authOpenIDClientID: this.authOpenIDClientID, // Do not return to client
|
||||||
authOpenIDClientSecret: this.authOpenIDClientSecret, // Do not return to client
|
authOpenIDClientSecret: this.authOpenIDClientSecret // Do not return to client
|
||||||
authOpenIDCallbackURL: this.authOpenIDCallbackURL
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,13 +239,42 @@ class ServerSettings {
|
|||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get authenticationSettings() {
|
||||||
|
return {
|
||||||
|
authActiveAuthMethods: this.authActiveAuthMethods,
|
||||||
|
authGoogleOauth20ClientID: this.authGoogleOauth20ClientID, // Do not return to client
|
||||||
|
authGoogleOauth20ClientSecret: this.authGoogleOauth20ClientSecret, // Do not return to client
|
||||||
|
authGoogleOauth20CallbackURL: this.authGoogleOauth20CallbackURL,
|
||||||
|
authOpenIDIssuerURL: this.authOpenIDIssuerURL,
|
||||||
|
authOpenIDAuthorizationURL: this.authOpenIDAuthorizationURL,
|
||||||
|
authOpenIDTokenURL: this.authOpenIDTokenURL,
|
||||||
|
authOpenIDUserInfoURL: this.authOpenIDUserInfoURL,
|
||||||
|
authOpenIDClientID: this.authOpenIDClientID, // Do not return to client
|
||||||
|
authOpenIDClientSecret: this.authOpenIDClientSecret // Do not return to client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update server settings
|
||||||
|
*
|
||||||
|
* @param {Object} payload
|
||||||
|
* @returns {boolean} true if updates were made
|
||||||
|
*/
|
||||||
update(payload) {
|
update(payload) {
|
||||||
let hasUpdates = false
|
let hasUpdates = false
|
||||||
for (const key in payload) {
|
for (const key in payload) {
|
||||||
if (key === 'sortingPrefixes' && payload[key] && payload[key].length) {
|
if (key === 'sortingPrefixes') {
|
||||||
const prefixesCleaned = payload[key].filter(prefix => !!prefix).map(prefix => prefix.toLowerCase())
|
// Sorting prefixes are updated with the /api/sorting-prefixes endpoint
|
||||||
if (prefixesCleaned.join(',') !== this[key].join(',')) {
|
continue
|
||||||
this[key] = [...prefixesCleaned]
|
} else if (key === 'authActiveAuthMethods') {
|
||||||
|
if (!payload[key]?.length) {
|
||||||
|
Logger.error(`[ServerSettings] Invalid authActiveAuthMethods`, payload[key])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
this.authActiveAuthMethods.sort()
|
||||||
|
payload[key].sort()
|
||||||
|
if (payload[key].join() !== this.authActiveAuthMethods.join()) {
|
||||||
|
this.authActiveAuthMethods = payload[key]
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
}
|
}
|
||||||
} else if (this[key] !== payload[key]) {
|
} else if (this[key] !== payload[key]) {
|
||||||
|
@ -306,6 +306,7 @@ class ApiRouter {
|
|||||||
this.router.post('/genres/rename', MiscController.renameGenre.bind(this))
|
this.router.post('/genres/rename', MiscController.renameGenre.bind(this))
|
||||||
this.router.delete('/genres/:genre', MiscController.deleteGenre.bind(this))
|
this.router.delete('/genres/:genre', MiscController.deleteGenre.bind(this))
|
||||||
this.router.post('/validate-cron', MiscController.validateCronExpression.bind(this))
|
this.router.post('/validate-cron', MiscController.validateCronExpression.bind(this))
|
||||||
|
this.router.get('/auth-settings', MiscController.getAuthSettings.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDirectories(dir, relpath, excludedDirs, level = 0) {
|
async getDirectories(dir, relpath, excludedDirs, level = 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user