Auth/OpenID: Implement Permissions via OpenID

* Ability to set group
* Ability to set more advanced permissions
* Modified TextInputWithLabel to provide an ability to specify a different placeholder then the name
This commit is contained in:
Denis Arnst 2024-03-19 17:57:24 +01:00
parent 8e5b7504ae
commit 56f1bfef50
No known key found for this signature in database
GPG key ID: D5866C58940197BF
5 changed files with 210 additions and 8 deletions

View file

@ -1,6 +1,7 @@
const packageJson = require('../../../package.json')
const { BookshelfView } = require('../../utils/constants')
const Logger = require('../../Logger')
const User = require('../user/User')
class ServerSettings {
constructor(settings) {
@ -72,6 +73,8 @@ class ServerSettings {
this.authOpenIDAutoRegister = false
this.authOpenIDMatchExistingBy = null
this.authOpenIDMobileRedirectURIs = ['audiobookshelf://oauth']
this.authOpenIDGroupClaim = ''
this.authOpenIDAdvancedPermsClaim = ''
if (settings) {
this.construct(settings)
@ -129,6 +132,8 @@ class ServerSettings {
this.authOpenIDAutoRegister = !!settings.authOpenIDAutoRegister
this.authOpenIDMatchExistingBy = settings.authOpenIDMatchExistingBy || null
this.authOpenIDMobileRedirectURIs = settings.authOpenIDMobileRedirectURIs || ['audiobookshelf://oauth']
this.authOpenIDGroupClaim = settings.authOpenIDGroupClaim || ''
this.authOpenIDAdvancedPermsClaim = settings.authOpenIDAdvancedPermsClaim || ''
if (!Array.isArray(this.authActiveAuthMethods)) {
this.authActiveAuthMethods = ['local']
@ -216,7 +221,9 @@ class ServerSettings {
authOpenIDAutoLaunch: this.authOpenIDAutoLaunch,
authOpenIDAutoRegister: this.authOpenIDAutoRegister,
authOpenIDMatchExistingBy: this.authOpenIDMatchExistingBy,
authOpenIDMobileRedirectURIs: this.authOpenIDMobileRedirectURIs // Do not return to client
authOpenIDMobileRedirectURIs: this.authOpenIDMobileRedirectURIs, // Do not return to client
authOpenIDGroupClaim: this.authOpenIDGroupClaim, // Do not return to client
authOpenIDAdvancedPermsClaim: this.authOpenIDAdvancedPermsClaim // Do not return to client
}
}
@ -226,6 +233,8 @@ class ServerSettings {
delete json.authOpenIDClientID
delete json.authOpenIDClientSecret
delete json.authOpenIDMobileRedirectURIs
delete json.authOpenIDGroupClaim
delete json.authOpenIDAdvancedPermsClaim
return json
}
@ -262,7 +271,11 @@ class ServerSettings {
authOpenIDAutoLaunch: this.authOpenIDAutoLaunch,
authOpenIDAutoRegister: this.authOpenIDAutoRegister,
authOpenIDMatchExistingBy: this.authOpenIDMatchExistingBy,
authOpenIDMobileRedirectURIs: this.authOpenIDMobileRedirectURIs // Do not return to client
authOpenIDMobileRedirectURIs: this.authOpenIDMobileRedirectURIs, // Do not return to client
authOpenIDGroupClaim: this.authOpenIDGroupClaim, // Do not return to client
authOpenIDAdvancedPermsClaim: this.authOpenIDAdvancedPermsClaim, // Do not return to client
authOpenIDSamplePermissions: User.getSampleAbsPermissions()
}
}

View file

@ -268,6 +268,78 @@ class User {
return hasUpdates
}
// List of expected permission properties from the client
static permissionMapping = {
canDownload: 'download',
canUpload: 'upload',
canDelete: 'delete',
canUpdate: 'update',
canAccessExplicitContent: 'accessExplicitContent',
canAccessAllLibraries: 'accessAllLibraries',
canAccessAllTags: 'accessAllTags',
tagsAreBlacklist: 'selectedTagsNotAccessible',
// Direct mapping for array-based permissions
allowedLibraries: 'librariesAccessible',
allowedTags: 'itemTagsSelected',
}
/**
* Update user from external JSON
*
* @param {object} absPermissions JSON containg user permissions
*/
updatePermissionsFromExternalJSON(absPermissions) {
// Initialize all permissions to false first
Object.keys(User.permissionMapping).forEach(mappingKey => {
const userPermKey = User.permissionMapping[mappingKey];
if (typeof this.permissions[userPermKey] === 'boolean') {
this.permissions[userPermKey] = false; // Default to false for boolean permissions
} else {
this[userPermKey] = []; // Default to empty array for other properties
}
});
Object.keys(absPermissions).forEach(absKey => {
const userPermKey = User.permissionMapping[absKey]
if (!userPermKey) {
throw new Error(`Unexpected permission property: ${absKey}`)
}
// Update the user's permissions based on absPermissions
this.permissions[userPermKey] = absPermissions[absKey]
});
// Handle allowedLibraries and allowedTags separately if needed
if (absPermissions.allowedLibraries) {
this.librariesAccessible = absPermissions.allowedLibraries
}
if (absPermissions.allowedTags) {
this.itemTagsSelected = absPermissions.allowedTags
}
}
/**
* Get a sample to show how a JSON for updatePermissionsFromExternalJSON should look like
*
* @returns JSON string
*/
static getSampleAbsPermissions() {
// Start with a template object where all permissions are false for simplicity
const samplePermissions = Object.keys(User.permissionMapping).reduce((acc, key) => {
// For array-based permissions, provide a sample array
if (key === 'allowedLibraries') {
acc[key] = [`ExampleLibrary`, `AnotherLibrary`];
} else if (key === 'allowedTags') {
acc[key] = [`ExampleTag`, `AnotherTag`, `ThirdTag`];
} else {
acc[key] = false;
}
return acc;
}, {});
return JSON.stringify(samplePermissions, null, 2); // Pretty print the JSON
}
/**
* Get first available library id for user
*