From 3cd09eecbae8016060c1d1c5f3ba323a166df808 Mon Sep 17 00:00:00 2001
From: David Leimroth <>
Date: Thu, 10 Feb 2022 14:45:21 +0100
Subject: [PATCH] adjusted SSOSettings model and changed all code based on the
model
---
client/layouts/default.vue | 3 +
client/pages/config/sso.vue | 244 +++++++++++++++++++++++-----------
client/pages/login.vue | 1 -
client/store/sso.js | 23 ++--
server/ApiController.js | 8 +-
server/Auth.js | 7 +-
server/Server.js | 4 +
server/objects/SSOSettings.js | 61 +++++++--
server/objects/User.js | 3 +
9 files changed, 251 insertions(+), 103 deletions(-)
diff --git a/client/layouts/default.vue b/client/layouts/default.vue
index 67727b840..0c4d545a5 100644
--- a/client/layouts/default.vue
+++ b/client/layouts/default.vue
@@ -112,6 +112,9 @@ export default {
if (payload.serverSettings) {
this.$store.commit('setServerSettings', payload.serverSettings)
}
+ if (payload.SSOSettings) {
+ this.$store.commit('sso/setSSOSettings', payload.SSOSettings)
+ }
// Start scans currently running
if (payload.librariesScanning) {
diff --git a/client/pages/config/sso.vue b/client/pages/config/sso.vue
index 97fca8eb4..ccb09609b 100644
--- a/client/pages/config/sso.vue
+++ b/client/pages/config/sso.vue
@@ -9,32 +9,32 @@
@@ -42,32 +42,32 @@
-
+
Create a new user on first login
-
+
The new user is allowed to download
-
+
The new user is allowed to update
-
+
The new user is allowed to delete
-
+
The new user is allowed to upload
-
+
The new user is allowed to access all libraries
@@ -90,16 +90,28 @@ export default {
clientID: "",
clientSecret: "",
},
-
- permissions: {
+ user: {
createNewUser: false,
- download: false,
- update: false,
- delete: false,
- upload: false,
- accessAllLibraries: false
+ isActive: true,
+ settings: {
+ mobileOrderBy: 'recent',
+ mobileOrderDesc: true,
+ mobileFilterBy: 'all',
+ orderBy: 'book.title',
+ orderDesc: false,
+ filterBy: 'all',
+ playbackRate: 1,
+ bookshelfCoverSize: 120,
+ collapseSeries: false
+ },
+ permissions: {
+ download: false,
+ update: false,
+ delete: false,
+ upload: false,
+ accessAllLibraries: false
+ }
},
-
updatingSSOSettings: false,
newSSOSettings: {}
}
@@ -114,55 +126,136 @@ export default {
},
computed: {
SSOSettings() {
- return this.$store.state.SSOSettings
+ return this.$store.state.sso
+ },
+
+ issuer: {
+ get() {
+ return this.oidc.issuer
+ return this.$store.state.sso.oidc.issuer
+ },
+ set(val) {
+ this.oidc.issuer = val
+ // this.$store.state.sso.oidc.issuer = val
+ }
+ },
+ authorizationURL: {
+ get() {
+ return this.oidc.authorizationURL
+ return this.$store.state.sso.oidc.authorizationURL
+ },
+ set(val) {
+ this.oidc.authorizationURL = val
+ // this.$store.state.sso.oidc.authorizationURL = val
+ }
+ },
+ tokenURL: {
+ get() {
+ return this.oidc.tokenURL
+ return this.$store.state.sso.oidc.tokenURL
+ },
+ set(val) {
+ this.oidc.tokenURL = val
+ // this.$store.state.sso.oidc.tokenURL = val
+ }
+ },
+ userInfoURL: {
+ get() {
+ return this.oidc.userInfoURL
+ return this.$store.state.sso.oidc.userInfoURL
+ },
+ set(val) {
+ this.oidc.userInfoURL = val
+ // this.$store.state.sso.oidc.userInfoURL = val
+ },
+ },
+ clientID: {
+ get() {
+ return this.oidc.clientID
+ return this.$store.state.sso.oidc.clientID
+ },
+ set(val) {
+ this.oidc.clientID = val
+ // this.$store.state.sso.oidc.clientID = val
+ },
+ },
+ clientSecret: {
+ get() {
+ return this.oidc.clientSecret
+ return this.$store.state.sso.oidc.clientSecret
+ },
+ set(val) {
+ this.oidc.clientSecret = val
+ // this.$store.state.sso.oidc.clientSecret = val
+ },
+ },
+ createNewUser: {
+ get() {
+ return this.user.createNewUser
+ return this.$store.state.sso.createNewUser
+ },
+ set(val) {
+ this.user.createNewUser = val
+ // this.$store.state.sso.createNewUser = val
+ },
+ },
+ permissionDownload: {
+ get() {
+ return this.user.permissions.download
+ return this.$store.state.sso.permissions.download
+ },
+ set(val) {
+ this.user.permissions.download = val
+ // this.$store.state.sso.permissions.download = val
+ },
+ },
+ permissionUpdate: {
+ get() {
+ return this.user.permissions.update
+ return this.$store.state.sso.permissions.update
+ },
+ set(val) {
+ this.user.permissions.update = val
+ // this.$store.state.sso.permissions.update = val
+ },
+ },
+ permissionDelete: {
+ get() {
+ return this.user.permissions.delete
+ return this.$store.state.sso.permissions.delete
+ },
+ set(val) {
+ this.user.permissions.delete = val
+ // this.$store.state.sso.permissions.delete = val
+ },
+ },
+ permissionUpload: {
+ get() {
+ return this.user.permissions.upload
+ return this.$store.state.sso.permissions.upload
+ },
+ set(val) {
+ this.user.permissions.upload = val
+ // this.$store.state.sso.permissions.upload = val
+ }
+ },
+ permissionAccessAllLibraries: {
+ get() {
+ return this.user.permissions.accessAllLibraries
+ return this.$store.state.sso.permissions.accessAllLibraries
+ },
+ set(val) {
+ this.user.permissions.accessAllLibraries = val
+ // this.$store.state.sso.permissions.accessAllLibraries = val
+ }
},
},
methods: {
- updateSSOIssuer(val) {
- this.oidc.issuer = val
- },
- updateAuthorizationURL(val) {
- this.oidc.authorizationURL = val
- },
- updateTokenURL(val) {
- this.oidc.tokenURL = val
- },
- updateUserInfoURL(val) {
- this.oidc.userInfoURL = val
- },
- updateClientID(val) {
- this.oidc.clientID = val
- },
- updateClientSecret(val) {
- this.oidc.clientSecret = val
- },
- updatePermissionCreateNewUser(val) {
- this.permissions.createNewUser = val
- },
- updatePermissionDownload(val) {
- this.permissions.createNewUser = val
- },
- updatePermissionUpdate(val) {
- this.permissions.createNewUser = val
- },
- updatePermissionDelete(val) {
- this.permissions.createNewUser = val
- },
- updatePermissionUpload(val) {
- this.permissions.createNewUser = val
- },
- updatePermissionAccessAllLibraries(val) {
- this.permissions.createNewUser = val
- },
- updatePermissionCreateNewUser(val) {
- this.permissions.createNewUser = val
- },
saveSSOSettings(payload) {
this.updatingSSOSettings = true
this.$store
- .dispatch('sso/updateSSOSettings', {oidc: this.oidc, permissions: this.permissions})
+ .dispatch('sso/updateSSOSettings', {oidc: this.oidc, user: this.user})
.then((success) => {
- console.log('Updated SSO Settings', success)
this.updatingSSOSettings = false
})
.catch((error) => {
@@ -171,20 +264,21 @@ export default {
})
},
initSSOSettings() {
- this.newSSOSettings = this.SSOSettings ? { ...this.SSOSettings } : {}
+ for (const key in this.$store.state.sso.oidc) {
+ this.oidc[key] = this.$store.state.sso.oidc[key]
+ }
-
- this.oidc.issuer = this.newSSOSettings.issuer
- this.oidc.authorizationURL = this.newSSOSettings.authorizationURL
- this.oidc.tokenURL = this.newSSOSettings.tokenURL
- this.oidc.userInfoURL = this.newSSOSettings.userInfoURL
- this.oidc.clientID = this.newSSOSettings.clientID
- this.oidc.clientSecret = this.newSSOSettings.clientSecret
- this.updatingSSOSettings = this.newSSOSettings.updatingSSOSettings
-
- // this.newSSOSettings.coverDestination === this.$constants.CoverDestination.AUDIOBOOK
- // this.useSquareBookCovers = this.newSSOSettings.coverAspectRatio === this.$constants.BookCoverAspectRatio.SQUARE
- // this.useAlternativeBookshelfView = this.newSSOSettings.bookshelfView === this.$constants.BookshelfView.TITLES
+ for (const key in this.$store.state.sso.user) {
+ if (typeof this.$store.state.sso.user[key] === "object" && typeof this.user[key] === "object") {
+ for (const key2 in this.$store.state.sso.user[key]) {
+ this.user[key][key2] = this.$store.state.sso.user[key][key2]
+ }
+ continue
+ }
+ if (this.user[key] !== undefined) {
+ this.user[key] = this.$store.state.sso.user[key]
+ }
+ }
},
},
diff --git a/client/pages/login.vue b/client/pages/login.vue
index f6b1dadd0..35e00726e 100644
--- a/client/pages/login.vue
+++ b/client/pages/login.vue
@@ -111,7 +111,6 @@ export default {
this.$axios
.$post('/api/authorize', null, {})
.then((res) => {
- console.log({res})
this.setUser(res.user)
this.processing = false
})
diff --git a/client/store/sso.js b/client/store/sso.js
index 7cb806c15..7bd70812b 100644
--- a/client/store/sso.js
+++ b/client/store/sso.js
@@ -87,20 +87,21 @@ export const actions = {
export const mutations = {
setSSOSettings(state, settings) {
- if (!settings) return
+ if (!settings || !settings.oidc || !settings.user) return
- for (const key in settings) {
- if (state.oidc[key] !== settings[key]) {
- state.oidc[key] = settings[key]
- }
+ for (const key in settings.oidc) {
+ state.oidc[key] = settings.oidc[key]
}
- },
- setUserPermissions(state, permissions) {
- if (!permissions) return
- for (const key in permissions) {
- if (state.user.permissions[key] !== permissions[key]) {
- state.user.permissions[key] = permissions[key]
+ for (const key in settings.user) {
+ if (typeof settings.user[key] === "object" && typeof state.user[key] === "object") {
+ for (const key2 in settings.user[key]) {
+ state.user[key][key2] = settings.user[key][key2]
+ }
+ continue
+ }
+ if (state.user[key] !== undefined) {
+ state.user[key] = settings.user[key]
}
}
},
diff --git a/server/ApiController.js b/server/ApiController.js
index 808cd54c0..3dba0408c 100644
--- a/server/ApiController.js
+++ b/server/ApiController.js
@@ -297,18 +297,20 @@ class ApiController {
return res.sendStatus(403)
}
let SSOUpdate = req.body
+ Logger.debug("SSOUpdate=", SSOUpdate)
if (!SSOUpdate || !isObject(SSOUpdate)) {
return res.status(500).send('Invalid settings update object')
}
- console.log("SSOUpdate", JSON.stringify(SSOUpdate))
+
var madeUpdates = this.db.SSOSettings.update(SSOUpdate)
+ console.log("SSOUpdate", JSON.stringify(this.db.SSOSettings))
if (madeUpdates) {
- await this.db.updateEntity('sso', this.db.SSOUpdate)
+ await this.db.updateEntity('settings', this.db.SSOSettings)
}
return res.json({
success: true,
- SSOUpdate: this.db.SSOUpdate
+ SSOUpdate: this.db.SSOSettings
})
}
diff --git a/server/Auth.js b/server/Auth.js
index c684c3e85..9af0a817b 100644
--- a/server/Auth.js
+++ b/server/Auth.js
@@ -217,11 +217,12 @@ class Auth {
async handleOIDCVerification(issuer, profile, cb) {
Logger.debug(`[Auth] handleOIDCVerification ${issuer}`)
- let user = this.db.users.find(u => u.id === profile.id)
- if (!user && this.db.SSOSettings.createNewUser) {
+ let user = this.db.users.find(u => u.ssoId === profile.id)
+ if (!user && this.db.SSOSettings.user.createNewUser) {
// create a user
let account = {}
- account.id = profile.id
+ account.id = profile.username
+ account.ssoId = profile.id
account.username = profile.username
account.isActive = true
account.type = "guest"
diff --git a/server/Server.js b/server/Server.js
index fd1cc47d3..c853d4f4b 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -85,6 +85,9 @@ class Server {
return client.user.toJSONForPublic(this.streamManager.streams)
})
}
+ get SSOSettings() {
+ return this.db.SSOSettings
+ }
getClientsForUser(userId) {
return Object.values(this.clients).filter(c => c.user && c.user.id === userId)
@@ -679,6 +682,7 @@ class Server {
}
if (user.type === 'root') {
initialPayload.usersOnline = this.usersOnline
+ initialPayload.SSOSettings = this.SSOSettings
}
client.socket.emit('init', initialPayload)
diff --git a/server/objects/SSOSettings.js b/server/objects/SSOSettings.js
index 7a3df5106..1f65b3e53 100644
--- a/server/objects/SSOSettings.js
+++ b/server/objects/SSOSettings.js
@@ -1,22 +1,51 @@
const Logger = require('../Logger')
const User = require('./User')
const { isObject } = require('../utils')
+const { difference } = require('../utils/string')
const defaultSettings = {
createNewUser: false,
- userPermissions: User.getDefaultUserPermissions('guest')
+ user: {
+ createNewUser: false,
+ isActive: true,
+ settings: {
+ mobileOrderBy: 'recent',
+ mobileOrderDesc: true,
+ mobileFilterBy: 'all',
+ orderBy: 'book.title',
+ orderDesc: false,
+ filterBy: 'all',
+ playbackRate: 1,
+ bookshelfCoverSize: 120,
+ collapseSeries: false
+ },
+ permissions: User.getDefaultUserPermissions('guest')
+ }
}
class SSOSettings {
constructor(settings = defaultSettings) {
this.id = 'sso-settings'
- this.createNewUser = !!settings.createNewUser
- this.userPermissions = { ...settings.userPermissions }
- this.initOIDCSettings();
+ this.user = { ...settings.user }
+ this.initOIDCSettings(settings);
+ Logger.debug("[SSOSettings.constructor]", this.toJSON)
}
- initOIDCSettings() {
+ initOIDCSettings(settings) {
// can't be part of default settings, because apperently process.env is not set in the beginning
+ if (settings && settings.oidc) {
+ this.oidc = {
+ issuer: settings.oidc.issuer,
+ authorizationURL: settings.oidc.authorizationURL,
+ tokenURL: settings.oidc.tokenURL,
+ userInfoURL: settings.oidc.userInfoURL,
+ clientID: settings.oidc.clientID,
+ clientSecret: settings.oidc.clientSecret,
+ callbackURL: "/oidc/callback",
+ scope: "openid email profile"
+ }
+ return
+ }
this.oidc = {
issuer: process.env.OIDC_ISSUER || '',
authorizationURL: process.env.OIDC_AUTHORIZATION_URL || '',
@@ -34,17 +63,28 @@ class SSOSettings {
}
toJSON() {
- return {
+ const tmp = {
id: this.id,
oidc: { ...this.oidc },
- createNewUser: this.createNewUser,
- userPermissions: { ...this.userPermissions }
+ user: { ...this.user }
}
+ return tmp
}
update(payload) {
- let hasUpdates = false
+ const oldTmp = JSON.stringify(this.toJSON())
+ const newTmp = JSON.stringify(payload) // deep copy "for free"
+ const hasUpdates = difference(oldTmp, newTmp) !== ""; // Not very efficient, but ok for small objects
+ Logger.debug(`SSOSettings hasUpdates=${hasUpdates}`)
+ if (!hasUpdates) return hasUpdates
+
+ payload = JSON.parse(newTmp)
+ this.oidc = payload.oidc
+ this.user = payload.user
+ return hasUpdates
+ Logger.debug("SSOSettings.update", payload, this)
for (const key in payload) {
+ Logger.debug(`key: ${key}: ${JSON.stringify(payload[key])}`)
if (isObject(payload[key])) {
for (const setting in payload[key]) {
if (!this[key] || this[key][setting] === payload[key][setting]) {
@@ -58,12 +98,13 @@ class SSOSettings {
hasUpdates = true
}
}
+ Logger.debug("SSOSettings.update", hasUpdates, this)
return hasUpdates
}
getNewUserPermissions() {
return {
- ...this.userPermissions
+ ...this.user.permissions
}
}
diff --git a/server/objects/User.js b/server/objects/User.js
index ddb53b8c2..2196007e4 100644
--- a/server/objects/User.js
+++ b/server/objects/User.js
@@ -14,6 +14,7 @@ class User {
this.lastSeen = null
this.createdAt = null
this.audiobooks = null
+ this.ssoId = null
this.settings = {}
this.permissions = {}
@@ -84,6 +85,7 @@ class User {
toJSON() {
return {
id: this.id,
+ ssoId: this.ssoId,
username: this.username,
pash: this.pash,
type: this.type,
@@ -138,6 +140,7 @@ class User {
this.type = user.type
this.stream = user.stream || null
this.token = user.token
+ this.ssoId = user.ssoId || ""
if (user.audiobooks) {
this.audiobooks = {}
for (const key in user.audiobooks) {