mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-04-20 01:17:45 +02:00
Merge pull request #2769 from Sapd/openid-permissions
OpenID: Integrate permissions (Fixes #2523)
This commit is contained in:
commit
a9c9c447f1
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-4 mb-8">
|
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-2 sm:p-4 mb-8">
|
||||||
<div class="flex items-center mb-2">
|
<div class="flex items-center mb-2">
|
||||||
<slot name="header-prefix"></slot>
|
<slot name="header-prefix"></slot>
|
||||||
<h1 class="text-xl">{{ headerText }}</h1>
|
<h1 class="text-xl">{{ headerText }}</h1>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
>{{ label }}<em v-if="note" class="font-normal text-xs pl-2">{{ note }}</em></label
|
>{{ label }}<em v-if="note" class="font-normal text-xs pl-2">{{ note }}</em></label
|
||||||
>
|
>
|
||||||
</slot>
|
</slot>
|
||||||
<ui-text-input :placeholder="label" :inputId="identifier" ref="input" v-model="inputValue" :disabled="disabled" :readonly="readonly" :type="type" class="w-full" :class="inputClass" @blur="inputBlurred" />
|
<ui-text-input :placeholder="placeholder || label" :inputId="identifier" ref="input" v-model="inputValue" :disabled="disabled" :readonly="readonly" :type="type" class="w-full" :class="inputClass" @blur="inputBlurred" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
value: [String, Number],
|
value: [String, Number],
|
||||||
label: String,
|
label: String,
|
||||||
|
placeholder: String,
|
||||||
note: String,
|
note: String,
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -59,28 +59,49 @@
|
|||||||
<ui-text-input-with-label ref="openidClientSecret" v-model="newAuthSettings.authOpenIDClientSecret" :disabled="savingSettings" :label="'Client Secret'" class="mb-2" />
|
<ui-text-input-with-label ref="openidClientSecret" v-model="newAuthSettings.authOpenIDClientSecret" :disabled="savingSettings" :label="'Client Secret'" class="mb-2" />
|
||||||
|
|
||||||
<ui-multi-select ref="redirectUris" v-model="newAuthSettings.authOpenIDMobileRedirectURIs" :items="newAuthSettings.authOpenIDMobileRedirectURIs" :label="$strings.LabelMobileRedirectURIs" class="mb-2" :menuDisabled="true" :disabled="savingSettings" />
|
<ui-multi-select ref="redirectUris" v-model="newAuthSettings.authOpenIDMobileRedirectURIs" :items="newAuthSettings.authOpenIDMobileRedirectURIs" :label="$strings.LabelMobileRedirectURIs" class="mb-2" :menuDisabled="true" :disabled="savingSettings" />
|
||||||
<p class="pl-4 text-sm text-gray-300 mb-2" v-html="$strings.LabelMobileRedirectURIsDescription" />
|
<p class="sm:pl-4 text-sm text-gray-300 mb-2" v-html="$strings.LabelMobileRedirectURIsDescription" />
|
||||||
|
|
||||||
<ui-text-input-with-label ref="buttonTextInput" v-model="newAuthSettings.authOpenIDButtonText" :disabled="savingSettings" :label="$strings.LabelButtonText" class="mb-2" />
|
<ui-text-input-with-label ref="buttonTextInput" v-model="newAuthSettings.authOpenIDButtonText" :disabled="savingSettings" :label="$strings.LabelButtonText" class="mb-2" />
|
||||||
|
|
||||||
<div class="flex items-center pt-1 mb-2">
|
<div class="flex sm:items-center flex-col sm:flex-row pt-1 mb-2">
|
||||||
<div class="w-44">
|
<div class="w-44">
|
||||||
<ui-dropdown v-model="newAuthSettings.authOpenIDMatchExistingBy" small :items="matchingExistingOptions" :label="$strings.LabelMatchExistingUsersBy" :disabled="savingSettings" />
|
<ui-dropdown v-model="newAuthSettings.authOpenIDMatchExistingBy" small :items="matchingExistingOptions" :label="$strings.LabelMatchExistingUsersBy" :disabled="savingSettings" />
|
||||||
</div>
|
</div>
|
||||||
<p class="pl-4 text-sm text-gray-300 mt-5">{{ $strings.LabelMatchExistingUsersByDescription }}</p>
|
<p class="sm:pl-4 text-sm text-gray-300 mt-2 sm:mt-5">{{ $strings.LabelMatchExistingUsersByDescription }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center py-4 px-1">
|
<div class="flex items-center py-4 px-1 w-full">
|
||||||
<ui-toggle-switch labeledBy="auto-redirect-toggle" v-model="newAuthSettings.authOpenIDAutoLaunch" :disabled="savingSettings" />
|
<ui-toggle-switch labeledBy="auto-redirect-toggle" v-model="newAuthSettings.authOpenIDAutoLaunch" :disabled="savingSettings" />
|
||||||
<p id="auto-redirect-toggle" class="pl-4 whitespace-nowrap">{{ $strings.LabelAutoLaunch }}</p>
|
<p id="auto-redirect-toggle" class="pl-4 whitespace-nowrap">{{ $strings.LabelAutoLaunch }}</p>
|
||||||
<p class="pl-4 text-sm text-gray-300" v-html="$strings.LabelAutoLaunchDescription" />
|
<p class="pl-4 text-sm text-gray-300" v-html="$strings.LabelAutoLaunchDescription" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center py-4 px-1">
|
<div class="flex items-center py-4 px-1 w-full">
|
||||||
<ui-toggle-switch labeledBy="auto-register-toggle" v-model="newAuthSettings.authOpenIDAutoRegister" :disabled="savingSettings" />
|
<ui-toggle-switch labeledBy="auto-register-toggle" v-model="newAuthSettings.authOpenIDAutoRegister" :disabled="savingSettings" />
|
||||||
<p id="auto-register-toggle" class="pl-4 whitespace-nowrap">{{ $strings.LabelAutoRegister }}</p>
|
<p id="auto-register-toggle" class="pl-4 whitespace-nowrap">{{ $strings.LabelAutoRegister }}</p>
|
||||||
<p class="pl-4 text-sm text-gray-300">{{ $strings.LabelAutoRegisterDescription }}</p>
|
<p class="pl-4 text-sm text-gray-300">{{ $strings.LabelAutoRegisterDescription }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p class="pt-6 mb-4 px-1">{{ $strings.LabelOpenIDClaims }}</p>
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row mb-4">
|
||||||
|
<div class="w-44 min-w-44">
|
||||||
|
<ui-text-input-with-label ref="openidGroupClaim" v-model="newAuthSettings.authOpenIDGroupClaim" :disabled="savingSettings" :placeholder="'groups'" :label="'Group Claim'" />
|
||||||
|
</div>
|
||||||
|
<p class="sm:pl-4 pt-2 sm:pt-0 text-sm text-gray-300" v-html="$strings.LabelOpenIDGroupClaimDescription"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row mb-4">
|
||||||
|
<div class="w-44 min-w-44">
|
||||||
|
<ui-text-input-with-label ref="openidAdvancedPermsClaim" v-model="newAuthSettings.authOpenIDAdvancedPermsClaim" :disabled="savingSettings" :placeholder="'abspermissions'" :label="'Advanced Permission Claim'" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:pl-4 pt-2 sm:pt-0 text-sm text-gray-300">
|
||||||
|
<p v-html="$strings.LabelOpenIDAdvancedPermsClaimDescription"></p>
|
||||||
|
<pre class="text-pre-wrap mt-2"
|
||||||
|
>{{ newAuthSettings.authOpenIDSamplePermissions }}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -222,6 +243,22 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidClaim(claim) {
|
||||||
|
if (claim === '') return true
|
||||||
|
|
||||||
|
const pattern = new RegExp('^[a-zA-Z][a-zA-Z0-9_-]*$', 'i')
|
||||||
|
return pattern.test(claim)
|
||||||
|
}
|
||||||
|
if (!isValidClaim(this.newAuthSettings.authOpenIDGroupClaim)) {
|
||||||
|
this.$toast.error('Group Claim: Invalid claim name')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
if (!isValidClaim(this.newAuthSettings.authOpenIDAdvancedPermsClaim)) {
|
||||||
|
this.$toast.error('Advanced Permission Claim: Invalid claim name')
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
return isValid
|
return isValid
|
||||||
},
|
},
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Nezahájeno",
|
"LabelNotStarted": "Nezahájeno",
|
||||||
"LabelNumberOfBooks": "Počet knih",
|
"LabelNumberOfBooks": "Počet knih",
|
||||||
"LabelNumberOfEpisodes": "Počet epizod",
|
"LabelNumberOfEpisodes": "Počet epizod",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Otevřít RSS kanál",
|
"LabelOpenRSSFeed": "Otevřít RSS kanál",
|
||||||
"LabelOverwrite": "Přepsat",
|
"LabelOverwrite": "Přepsat",
|
||||||
"LabelPassword": "Heslo",
|
"LabelPassword": "Heslo",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Ikke påbegyndt",
|
"LabelNotStarted": "Ikke påbegyndt",
|
||||||
"LabelNumberOfBooks": "Antal bøger",
|
"LabelNumberOfBooks": "Antal bøger",
|
||||||
"LabelNumberOfEpisodes": "Antal episoder",
|
"LabelNumberOfEpisodes": "Antal episoder",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Åbn RSS-feed",
|
"LabelOpenRSSFeed": "Åbn RSS-feed",
|
||||||
"LabelOverwrite": "Overskriv",
|
"LabelOverwrite": "Overskriv",
|
||||||
"LabelPassword": "Kodeord",
|
"LabelPassword": "Kodeord",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Nicht begonnen",
|
"LabelNotStarted": "Nicht begonnen",
|
||||||
"LabelNumberOfBooks": "Anzahl der Hörbücher",
|
"LabelNumberOfBooks": "Anzahl der Hörbücher",
|
||||||
"LabelNumberOfEpisodes": "Anzahl der Episoden",
|
"LabelNumberOfEpisodes": "Anzahl der Episoden",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name des OpenID-Claims, der erweiterte Berechtigungen für Benutzeraktionen innerhalb der Anwendung enthält, die auf Nicht-Admin-Rollen angewendet werden (<b>wenn konfiguriert</b>). Wenn der Claim in der Antwort fehlt, wird der Zugang zu ABS verweigert. Fehlt eine einzelne Option, wird sie als <code>false</code> behandelt. Stelle sicher, dass der Claim des Identitätsanbieters der erwarteten Struktur entspricht:",
|
||||||
|
"LabelOpenIDClaims": "Lass die folgenden Optionen leer, um die erweiterte Zuweisung von Gruppen und Berechtigungen zu deaktivieren und automatisch die 'User'-Gruppe zuzuweisen.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name des OpenID-Claims, der eine Liste der Benutzergruppen enthält. Wird häufig als <code>groups</code> bezeichnet. <b>Wenn konfiguriert</b>, wird die Anwendung automatisch Rollen basierend auf den Gruppenmitgliedschaften des Benutzers zuweisen, vorausgesetzt, dass diese Gruppen im Claim als 'admin', 'user' oder 'guest' benannt sind (Groß/Kleinschreibung ist irrelevant). Der Claim eine Liste sein, und wenn ein Benutzer mehreren Gruppen angehört, wird die Anwendung die Rolle zuordnen, die dem höchsten Zugriffslevel entspricht. Wenn keine Gruppe übereinstimmt, wird der Zugang verweigert.",
|
||||||
"LabelOpenRSSFeed": "Öffne RSS-Feed",
|
"LabelOpenRSSFeed": "Öffne RSS-Feed",
|
||||||
"LabelOverwrite": "Überschreiben",
|
"LabelOverwrite": "Überschreiben",
|
||||||
"LabelPassword": "Passwort",
|
"LabelPassword": "Passwort",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Not Started",
|
"LabelNotStarted": "Not Started",
|
||||||
"LabelNumberOfBooks": "Number of Books",
|
"LabelNumberOfBooks": "Number of Books",
|
||||||
"LabelNumberOfEpisodes": "# of Episodes",
|
"LabelNumberOfEpisodes": "# of Episodes",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Open RSS Feed",
|
"LabelOpenRSSFeed": "Open RSS Feed",
|
||||||
"LabelOverwrite": "Overwrite",
|
"LabelOverwrite": "Overwrite",
|
||||||
"LabelPassword": "Password",
|
"LabelPassword": "Password",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Sin Iniciar",
|
"LabelNotStarted": "Sin Iniciar",
|
||||||
"LabelNumberOfBooks": "Numero de Libros",
|
"LabelNumberOfBooks": "Numero de Libros",
|
||||||
"LabelNumberOfEpisodes": "# de Episodios",
|
"LabelNumberOfEpisodes": "# de Episodios",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Abrir Fuente RSS",
|
"LabelOpenRSSFeed": "Abrir Fuente RSS",
|
||||||
"LabelOverwrite": "Sobrescribir",
|
"LabelOverwrite": "Sobrescribir",
|
||||||
"LabelPassword": "Contraseña",
|
"LabelPassword": "Contraseña",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Pole alustatud",
|
"LabelNotStarted": "Pole alustatud",
|
||||||
"LabelNumberOfBooks": "Raamatute arv",
|
"LabelNumberOfBooks": "Raamatute arv",
|
||||||
"LabelNumberOfEpisodes": "Episoodide arv",
|
"LabelNumberOfEpisodes": "Episoodide arv",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Ava RSS voog",
|
"LabelOpenRSSFeed": "Ava RSS voog",
|
||||||
"LabelOverwrite": "Kirjuta üle",
|
"LabelOverwrite": "Kirjuta üle",
|
||||||
"LabelPassword": "Parool",
|
"LabelPassword": "Parool",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Pas commencé",
|
"LabelNotStarted": "Pas commencé",
|
||||||
"LabelNumberOfBooks": "Nombre de livres",
|
"LabelNumberOfBooks": "Nombre de livres",
|
||||||
"LabelNumberOfEpisodes": "Nombre d’épisodes",
|
"LabelNumberOfEpisodes": "Nombre d’épisodes",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Ouvrir le flux RSS",
|
"LabelOpenRSSFeed": "Ouvrir le flux RSS",
|
||||||
"LabelOverwrite": "Écraser",
|
"LabelOverwrite": "Écraser",
|
||||||
"LabelPassword": "Mot de passe",
|
"LabelPassword": "Mot de passe",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Not Started",
|
"LabelNotStarted": "Not Started",
|
||||||
"LabelNumberOfBooks": "Number of Books",
|
"LabelNumberOfBooks": "Number of Books",
|
||||||
"LabelNumberOfEpisodes": "# of Episodes",
|
"LabelNumberOfEpisodes": "# of Episodes",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Open RSS Feed",
|
"LabelOpenRSSFeed": "Open RSS Feed",
|
||||||
"LabelOverwrite": "Overwrite",
|
"LabelOverwrite": "Overwrite",
|
||||||
"LabelPassword": "Password",
|
"LabelPassword": "Password",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "לא התחיל",
|
"LabelNotStarted": "לא התחיל",
|
||||||
"LabelNumberOfBooks": "מספר הספרים",
|
"LabelNumberOfBooks": "מספר הספרים",
|
||||||
"LabelNumberOfEpisodes": "מספר הפרקים",
|
"LabelNumberOfEpisodes": "מספר הפרקים",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "פתח ערוץ RSS",
|
"LabelOpenRSSFeed": "פתח ערוץ RSS",
|
||||||
"LabelOverwrite": "לשכפל",
|
"LabelOverwrite": "לשכפל",
|
||||||
"LabelPassword": "סיסמה",
|
"LabelPassword": "סיסמה",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Not Started",
|
"LabelNotStarted": "Not Started",
|
||||||
"LabelNumberOfBooks": "Number of Books",
|
"LabelNumberOfBooks": "Number of Books",
|
||||||
"LabelNumberOfEpisodes": "# of Episodes",
|
"LabelNumberOfEpisodes": "# of Episodes",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Open RSS Feed",
|
"LabelOpenRSSFeed": "Open RSS Feed",
|
||||||
"LabelOverwrite": "Overwrite",
|
"LabelOverwrite": "Overwrite",
|
||||||
"LabelPassword": "Password",
|
"LabelPassword": "Password",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Not Started",
|
"LabelNotStarted": "Not Started",
|
||||||
"LabelNumberOfBooks": "Number of Books",
|
"LabelNumberOfBooks": "Number of Books",
|
||||||
"LabelNumberOfEpisodes": "# of Episodes",
|
"LabelNumberOfEpisodes": "# of Episodes",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Otvori RSS Feed",
|
"LabelOpenRSSFeed": "Otvori RSS Feed",
|
||||||
"LabelOverwrite": "Overwrite",
|
"LabelOverwrite": "Overwrite",
|
||||||
"LabelPassword": "Lozinka",
|
"LabelPassword": "Lozinka",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Nem indult el",
|
"LabelNotStarted": "Nem indult el",
|
||||||
"LabelNumberOfBooks": "Könyvek száma",
|
"LabelNumberOfBooks": "Könyvek száma",
|
||||||
"LabelNumberOfEpisodes": "Epizódok száma",
|
"LabelNumberOfEpisodes": "Epizódok száma",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "RSS hírcsatorna megnyitása",
|
"LabelOpenRSSFeed": "RSS hírcsatorna megnyitása",
|
||||||
"LabelOverwrite": "Felülírás",
|
"LabelOverwrite": "Felülírás",
|
||||||
"LabelPassword": "Jelszó",
|
"LabelPassword": "Jelszó",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Non iniziato",
|
"LabelNotStarted": "Non iniziato",
|
||||||
"LabelNumberOfBooks": "Numero di libri",
|
"LabelNumberOfBooks": "Numero di libri",
|
||||||
"LabelNumberOfEpisodes": "# degli episodi",
|
"LabelNumberOfEpisodes": "# degli episodi",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Apri RSS Feed",
|
"LabelOpenRSSFeed": "Apri RSS Feed",
|
||||||
"LabelOverwrite": "Sovrascrivi",
|
"LabelOverwrite": "Sovrascrivi",
|
||||||
"LabelPassword": "Password",
|
"LabelPassword": "Password",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Nepasileista",
|
"LabelNotStarted": "Nepasileista",
|
||||||
"LabelNumberOfBooks": "Knygų skaičius",
|
"LabelNumberOfBooks": "Knygų skaičius",
|
||||||
"LabelNumberOfEpisodes": "Epizodų skaičius",
|
"LabelNumberOfEpisodes": "Epizodų skaičius",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Atidaryti RSS srautą",
|
"LabelOpenRSSFeed": "Atidaryti RSS srautą",
|
||||||
"LabelOverwrite": "Perrašyti",
|
"LabelOverwrite": "Perrašyti",
|
||||||
"LabelPassword": "Slaptažodis",
|
"LabelPassword": "Slaptažodis",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Niet Gestart",
|
"LabelNotStarted": "Niet Gestart",
|
||||||
"LabelNumberOfBooks": "Aantal Boeken",
|
"LabelNumberOfBooks": "Aantal Boeken",
|
||||||
"LabelNumberOfEpisodes": "# afleveringen",
|
"LabelNumberOfEpisodes": "# afleveringen",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Open RSS-feed",
|
"LabelOpenRSSFeed": "Open RSS-feed",
|
||||||
"LabelOverwrite": "Overschrijf",
|
"LabelOverwrite": "Overschrijf",
|
||||||
"LabelPassword": "Wachtwoord",
|
"LabelPassword": "Wachtwoord",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Ikke startet",
|
"LabelNotStarted": "Ikke startet",
|
||||||
"LabelNumberOfBooks": "Antall bøker",
|
"LabelNumberOfBooks": "Antall bøker",
|
||||||
"LabelNumberOfEpisodes": "Antall episoder",
|
"LabelNumberOfEpisodes": "Antall episoder",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Åpne RSS Feed",
|
"LabelOpenRSSFeed": "Åpne RSS Feed",
|
||||||
"LabelOverwrite": "Overskriv",
|
"LabelOverwrite": "Overskriv",
|
||||||
"LabelPassword": "Passord",
|
"LabelPassword": "Passord",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Nie rozpoęczto",
|
"LabelNotStarted": "Nie rozpoęczto",
|
||||||
"LabelNumberOfBooks": "Liczba książek",
|
"LabelNumberOfBooks": "Liczba książek",
|
||||||
"LabelNumberOfEpisodes": "# odcinków",
|
"LabelNumberOfEpisodes": "# odcinków",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Otwórz kanał RSS",
|
"LabelOpenRSSFeed": "Otwórz kanał RSS",
|
||||||
"LabelOverwrite": "Overwrite",
|
"LabelOverwrite": "Overwrite",
|
||||||
"LabelPassword": "Hasło",
|
"LabelPassword": "Hasło",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Não iniciado",
|
"LabelNotStarted": "Não iniciado",
|
||||||
"LabelNumberOfBooks": "Número de Livros",
|
"LabelNumberOfBooks": "Número de Livros",
|
||||||
"LabelNumberOfEpisodes": "# de Episódios",
|
"LabelNumberOfEpisodes": "# de Episódios",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Abrir Feed RSS",
|
"LabelOpenRSSFeed": "Abrir Feed RSS",
|
||||||
"LabelOverwrite": "Sobrescrever",
|
"LabelOverwrite": "Sobrescrever",
|
||||||
"LabelPassword": "Senha",
|
"LabelPassword": "Senha",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Не запущено",
|
"LabelNotStarted": "Не запущено",
|
||||||
"LabelNumberOfBooks": "Количество книг",
|
"LabelNumberOfBooks": "Количество книг",
|
||||||
"LabelNumberOfEpisodes": "# Эпизодов",
|
"LabelNumberOfEpisodes": "# Эпизодов",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Открыть RSS-канал",
|
"LabelOpenRSSFeed": "Открыть RSS-канал",
|
||||||
"LabelOverwrite": "Перезаписать",
|
"LabelOverwrite": "Перезаписать",
|
||||||
"LabelPassword": "Пароль",
|
"LabelPassword": "Пароль",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Inte påbörjad",
|
"LabelNotStarted": "Inte påbörjad",
|
||||||
"LabelNumberOfBooks": "Antal böcker",
|
"LabelNumberOfBooks": "Antal böcker",
|
||||||
"LabelNumberOfEpisodes": "Antal avsnitt",
|
"LabelNumberOfEpisodes": "Antal avsnitt",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Öppna RSS-flöde",
|
"LabelOpenRSSFeed": "Öppna RSS-flöde",
|
||||||
"LabelOverwrite": "Skriv över",
|
"LabelOverwrite": "Skriv över",
|
||||||
"LabelPassword": "Lösenord",
|
"LabelPassword": "Lösenord",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "Chưa bắt đầu",
|
"LabelNotStarted": "Chưa bắt đầu",
|
||||||
"LabelNumberOfBooks": "Số lượng Sách",
|
"LabelNumberOfBooks": "Số lượng Sách",
|
||||||
"LabelNumberOfEpisodes": "# của Tập",
|
"LabelNumberOfEpisodes": "# của Tập",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "Mở RSS Feed",
|
"LabelOpenRSSFeed": "Mở RSS Feed",
|
||||||
"LabelOverwrite": "Ghi đè",
|
"LabelOverwrite": "Ghi đè",
|
||||||
"LabelPassword": "Mật khẩu",
|
"LabelPassword": "Mật khẩu",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "未开始",
|
"LabelNotStarted": "未开始",
|
||||||
"LabelNumberOfBooks": "图书数量",
|
"LabelNumberOfBooks": "图书数量",
|
||||||
"LabelNumberOfEpisodes": "# 集",
|
"LabelNumberOfEpisodes": "# 集",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "打开 RSS 源",
|
"LabelOpenRSSFeed": "打开 RSS 源",
|
||||||
"LabelOverwrite": "覆盖",
|
"LabelOverwrite": "覆盖",
|
||||||
"LabelPassword": "密码",
|
"LabelPassword": "密码",
|
||||||
|
@ -385,6 +385,9 @@
|
|||||||
"LabelNotStarted": "未開始",
|
"LabelNotStarted": "未開始",
|
||||||
"LabelNumberOfBooks": "圖書數量",
|
"LabelNumberOfBooks": "圖書數量",
|
||||||
"LabelNumberOfEpisodes": "# 集",
|
"LabelNumberOfEpisodes": "# 集",
|
||||||
|
"LabelOpenIDAdvancedPermsClaimDescription": "Name of the OpenID claim that contains advanced permissions for user actions within the application which will apply to non-admin roles (<b>if configured</b>). If the claim is missing from the response, access to ABS will be denied. If a single option is missing, it will be treated as <code>false</code>. Ensure the identity provider's claim matches the expected structure:",
|
||||||
|
"LabelOpenIDClaims": "Leave the following options empty to disable advanced group and permissions assignment, automatically assigning 'User' group then.",
|
||||||
|
"LabelOpenIDGroupClaimDescription": "Name of the OpenID claim that contains a list of the user's groups. Commonly referred to as <code>groups</code>. <b>If configured</b>, the application will automatically assign roles based on the user's group memberships, provided that these groups are named case-insensitively 'admin', 'user', or 'guest' in the claim. The claim should contain a list, and if a user belongs to multiple groups, the application will assign the role corresponding to the highest level of access. If no group matches, access will be denied.",
|
||||||
"LabelOpenRSSFeed": "打開 RSS 源",
|
"LabelOpenRSSFeed": "打開 RSS 源",
|
||||||
"LabelOverwrite": "覆蓋",
|
"LabelOverwrite": "覆蓋",
|
||||||
"LabelPassword": "密碼",
|
"LabelPassword": "密碼",
|
||||||
|
240
server/Auth.js
240
server/Auth.js
@ -98,71 +98,198 @@ class Auth {
|
|||||||
scope: 'openid profile email'
|
scope: 'openid profile email'
|
||||||
}
|
}
|
||||||
}, async (tokenset, userinfo, done) => {
|
}, async (tokenset, userinfo, done) => {
|
||||||
Logger.debug(`[Auth] openid callback userinfo=`, userinfo)
|
try {
|
||||||
|
Logger.debug(`[Auth] openid callback userinfo=`, JSON.stringify(userinfo, null, 2))
|
||||||
|
|
||||||
let failureMessage = 'Unauthorized'
|
if (!userinfo.sub) {
|
||||||
if (!userinfo.sub) {
|
throw new Error('Invalid userinfo, no sub')
|
||||||
Logger.error(`[Auth] openid callback invalid userinfo, no sub`)
|
}
|
||||||
return done(null, null, failureMessage)
|
|
||||||
|
if (!this.validateGroupClaim(userinfo)) {
|
||||||
|
throw new Error(`Group claim ${Database.serverSettings.authOpenIDGroupClaim} not found or empty in userinfo`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = await this.findOrCreateUser(userinfo)
|
||||||
|
|
||||||
|
if (!user?.isActive) {
|
||||||
|
throw new Error('User not active or not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.setUserGroup(user, userinfo)
|
||||||
|
await this.updateUserPermissions(user, userinfo)
|
||||||
|
|
||||||
|
// We also have to save the id_token for later (used for logout) because we cannot set cookies here
|
||||||
|
user.openid_id_token = tokenset.id_token
|
||||||
|
|
||||||
|
return done(null, user)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(`[Auth] openid callback error: ${error?.message}\n${error?.stack}`)
|
||||||
|
|
||||||
|
return done(null, null, 'Unauthorized')
|
||||||
}
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
// First check for matching user by sub
|
/**
|
||||||
let user = await Database.userModel.getUserByOpenIDSub(userinfo.sub)
|
* Finds an existing user by OpenID subject identifier, or by email/username based on server settings,
|
||||||
if (!user) {
|
* or creates a new user if configured to do so.
|
||||||
// Optionally match existing by email or username based on server setting "authOpenIDMatchExistingBy"
|
*/
|
||||||
if (Database.serverSettings.authOpenIDMatchExistingBy === 'email' && userinfo.email && userinfo.email_verified) {
|
async findOrCreateUser(userinfo) {
|
||||||
|
let user = await Database.userModel.getUserByOpenIDSub(userinfo.sub)
|
||||||
|
|
||||||
|
// Matched by sub
|
||||||
|
if (user) {
|
||||||
|
Logger.debug(`[Auth] openid: User found by sub`)
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match existing user by email
|
||||||
|
if (Database.serverSettings.authOpenIDMatchExistingBy === 'email') {
|
||||||
|
if (userinfo.email) {
|
||||||
|
// Only disallow when email_verified explicitly set to false (allow both if not set or true)
|
||||||
|
if (userinfo.email_verified === false) {
|
||||||
|
Logger.warn(`[Auth] openid: User not found and email "${userinfo.email}" is not verified`)
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
Logger.info(`[Auth] openid: User not found, checking existing with email "${userinfo.email}"`)
|
Logger.info(`[Auth] openid: User not found, checking existing with email "${userinfo.email}"`)
|
||||||
user = await Database.userModel.getUserByEmail(userinfo.email)
|
user = await Database.userModel.getUserByEmail(userinfo.email)
|
||||||
// Check that user is not already matched
|
|
||||||
if (user?.authOpenIDSub) {
|
if (user?.authOpenIDSub) {
|
||||||
Logger.warn(`[Auth] openid: User found with email "${userinfo.email}" but is already matched with sub "${user.authOpenIDSub}"`)
|
Logger.warn(`[Auth] openid: User found with email "${userinfo.email}" but is already matched with sub "${user.authOpenIDSub}"`)
|
||||||
// TODO: Message isn't actually returned to the user yet. Need to override the passport authenticated callback
|
return null // User is linked to a different OpenID subject; do not proceed.
|
||||||
failureMessage = 'A matching user was found but is already matched with another user from your auth provider'
|
|
||||||
user = null
|
|
||||||
}
|
|
||||||
} else if (Database.serverSettings.authOpenIDMatchExistingBy === 'username' && userinfo.preferred_username) {
|
|
||||||
Logger.info(`[Auth] openid: User not found, checking existing with username "${userinfo.preferred_username}"`)
|
|
||||||
user = await Database.userModel.getUserByUsername(userinfo.preferred_username)
|
|
||||||
// Check that user is not already matched
|
|
||||||
if (user?.authOpenIDSub) {
|
|
||||||
Logger.warn(`[Auth] openid: User found with username "${userinfo.preferred_username}" but is already matched with sub "${user.authOpenIDSub}"`)
|
|
||||||
// TODO: Message isn't actually returned to the user yet. Need to override the passport authenticated callback
|
|
||||||
failureMessage = 'A matching user was found but is already matched with another user from your auth provider'
|
|
||||||
user = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Logger.warn(`[Auth] openid: User not found and no email in userinfo`)
|
||||||
|
// We deny login, because if the admin whishes to match email, it makes sense to require it
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Match existing user by username
|
||||||
|
else if (Database.serverSettings.authOpenIDMatchExistingBy === 'username') {
|
||||||
|
let username
|
||||||
|
|
||||||
// If existing user was matched and isActive then save sub to user
|
if (userinfo.preferred_username) {
|
||||||
if (user?.isActive) {
|
Logger.info(`[Auth] openid: User not found, checking existing with userinfo.preferred_username "${userinfo.preferred_username}"`)
|
||||||
Logger.info(`[Auth] openid: New user found matching existing user "${user.username}"`)
|
username = userinfo.preferred_username
|
||||||
user.authOpenIDSub = userinfo.sub
|
} else if (userinfo.username) {
|
||||||
await Database.userModel.updateFromOld(user)
|
Logger.info(`[Auth] openid: User not found, checking existing with userinfo.username "${userinfo.username}"`)
|
||||||
} else if (user && !user.isActive) {
|
username = userinfo.username
|
||||||
Logger.warn(`[Auth] openid: New user found matching existing user "${user.username}" but that user is deactivated`)
|
} else {
|
||||||
}
|
Logger.warn(`[Auth] openid: User not found and neither preferred_username nor username in userinfo`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// Optionally auto register the user
|
|
||||||
if (!user && Database.serverSettings.authOpenIDAutoRegister) {
|
user = await Database.userModel.getUserByUsername(username)
|
||||||
Logger.info(`[Auth] openid: Auto-registering user with sub "${userinfo.sub}"`, userinfo)
|
|
||||||
user = await Database.userModel.createUserFromOpenIdUserInfo(userinfo, this)
|
if (user?.authOpenIDSub) {
|
||||||
|
Logger.warn(`[Auth] openid: User found with username "${username}" but is already matched with sub "${user.authOpenIDSub}"`)
|
||||||
|
return null // User is linked to a different OpenID subject; do not proceed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found existing user via email or username
|
||||||
|
if (user) {
|
||||||
|
if (!user.isActive) {
|
||||||
|
Logger.warn(`[Auth] openid: User found but is not active`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
user.authOpenIDSub = userinfo.sub
|
||||||
|
await Database.userModel.updateFromOld(user)
|
||||||
|
|
||||||
|
Logger.debug(`[Auth] openid: User found by email/username`)
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no existing user was matched, auto-register if configured
|
||||||
|
if (Database.serverSettings.authOpenIDAutoRegister) {
|
||||||
|
Logger.info(`[Auth] openid: Auto-registering user with sub "${userinfo.sub}"`, userinfo)
|
||||||
|
user = await Database.userModel.createUserFromOpenIdUserInfo(userinfo, this)
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.warn(`[Auth] openid: User not found and auto-register is disabled`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the presence and content of the group claim in userinfo.
|
||||||
|
*/
|
||||||
|
validateGroupClaim(userinfo) {
|
||||||
|
const groupClaimName = Database.serverSettings.authOpenIDGroupClaim
|
||||||
|
if (!groupClaimName) // Allow no group claim when configured like this
|
||||||
|
return true
|
||||||
|
|
||||||
|
// If configured it must exist in userinfo
|
||||||
|
if (!userinfo[groupClaimName]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user group based on group claim in userinfo.
|
||||||
|
*
|
||||||
|
* @param {import('./objects/user/User')} user
|
||||||
|
* @param {Object} userinfo
|
||||||
|
*/
|
||||||
|
async setUserGroup(user, userinfo) {
|
||||||
|
const groupClaimName = Database.serverSettings.authOpenIDGroupClaim
|
||||||
|
if (!groupClaimName) // No group claim configured, don't set anything
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!userinfo[groupClaimName])
|
||||||
|
throw new Error(`Group claim ${groupClaimName} not found in userinfo`)
|
||||||
|
|
||||||
|
const groupsList = userinfo[groupClaimName].map(group => group.toLowerCase())
|
||||||
|
const rolesInOrderOfPriority = ['admin', 'user', 'guest']
|
||||||
|
|
||||||
|
let userType = rolesInOrderOfPriority.find(role => groupsList.includes(role))
|
||||||
|
if (userType) {
|
||||||
|
if (user.type === 'root') {
|
||||||
|
// Check OpenID Group
|
||||||
|
if (userType !== 'admin') {
|
||||||
|
throw new Error(`Root user "${user.username}" cannot be downgraded to ${userType}. Denying login.`)
|
||||||
|
} else {
|
||||||
|
// If root user is logging in via OpenID, we will not change the type
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user?.isActive) {
|
if (user.type !== userType) {
|
||||||
if (user && !user.isActive) {
|
Logger.info(`[Auth] openid callback: Updating user "${user.username}" type to "${userType}" from "${user.type}"`)
|
||||||
failureMessage = 'Unauthorized'
|
user.type = userType
|
||||||
}
|
await Database.userModel.updateFromOld(user)
|
||||||
// deny login
|
|
||||||
done(null, null, failureMessage)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`No valid group found in userinfo: ${JSON.stringify(userinfo[groupClaimName], null, 2)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We also have to save the id_token for later (used for logout) because we cannot set cookies here
|
/**
|
||||||
user.openid_id_token = tokenset.id_token
|
* Updates user permissions based on the advanced permissions claim.
|
||||||
|
*
|
||||||
|
* @param {import('./objects/user/User')} user
|
||||||
|
* @param {Object} userinfo
|
||||||
|
*/
|
||||||
|
async updateUserPermissions(user, userinfo) {
|
||||||
|
const absPermissionsClaim = Database.serverSettings.authOpenIDAdvancedPermsClaim
|
||||||
|
if (!absPermissionsClaim) // No advanced permissions claim configured, don't set anything
|
||||||
|
return
|
||||||
|
|
||||||
// permit login
|
if (user.type === 'admin' || user.type === 'root')
|
||||||
return done(null, user)
|
return
|
||||||
}))
|
|
||||||
|
const absPermissions = userinfo[absPermissionsClaim]
|
||||||
|
if (!absPermissions)
|
||||||
|
throw new Error(`Advanced permissions claim ${absPermissionsClaim} not found in userinfo`)
|
||||||
|
|
||||||
|
if (user.updatePermissionsFromExternalJSON(absPermissions)) {
|
||||||
|
Logger.info(`[Auth] openid callback: Updating advanced perms for user "${user.username}" using "${JSON.stringify(absPermissions)}"`)
|
||||||
|
await Database.userModel.updateFromOld(user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -334,10 +461,19 @@ class Auth {
|
|||||||
sso_redirect_uri: oidcStrategy._params.redirect_uri // Save the redirect_uri (for the SSO Provider) for the callback
|
sso_redirect_uri: oidcStrategy._params.redirect_uri // Save the redirect_uri (for the SSO Provider) for the callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scope = 'openid profile email'
|
||||||
|
if (global.ServerSettings.authOpenIDGroupClaim) {
|
||||||
|
scope += ' ' + global.ServerSettings.authOpenIDGroupClaim
|
||||||
|
}
|
||||||
|
if (global.ServerSettings.authOpenIDAdvancedPermsClaim) {
|
||||||
|
scope += ' ' + global.ServerSettings.authOpenIDAdvancedPermsClaim
|
||||||
|
}
|
||||||
|
|
||||||
const authorizationUrl = client.authorizationUrl({
|
const authorizationUrl = client.authorizationUrl({
|
||||||
...oidcStrategy._params,
|
...oidcStrategy._params,
|
||||||
state: state,
|
state: state,
|
||||||
response_type: 'code',
|
response_type: 'code',
|
||||||
|
scope: scope,
|
||||||
code_challenge,
|
code_challenge,
|
||||||
code_challenge_method
|
code_challenge_method
|
||||||
})
|
})
|
||||||
@ -346,7 +482,7 @@ class Auth {
|
|||||||
|
|
||||||
res.redirect(authorizationUrl)
|
res.redirect(authorizationUrl)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(`[Auth] Error in /auth/openid route: ${error}`)
|
Logger.error(`[Auth] Error in /auth/openid route: ${error}\n${error?.stack}`)
|
||||||
res.status(500).send('Internal Server Error')
|
res.status(500).send('Internal Server Error')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +538,7 @@ class Auth {
|
|||||||
// Redirect to the overwrite URI saved in the map
|
// Redirect to the overwrite URI saved in the map
|
||||||
res.redirect(redirectUri)
|
res.redirect(redirectUri)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(`[Auth] Error in /auth/openid/mobile-redirect route: ${error}`)
|
Logger.error(`[Auth] Error in /auth/openid/mobile-redirect route: ${error}\n${error?.stack}`)
|
||||||
res.status(500).send('Internal Server Error')
|
res.status(500).send('Internal Server Error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -424,12 +560,12 @@ class Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleAuthError(isMobile, errorCode, errorMessage, logMessage, response) {
|
function handleAuthError(isMobile, errorCode, errorMessage, logMessage, response) {
|
||||||
Logger.error(logMessage)
|
Logger.error(JSON.stringify(logMessage, null, 2))
|
||||||
if (response) {
|
if (response) {
|
||||||
// Depending on the error, it can also have a body
|
// Depending on the error, it can also have a body
|
||||||
// We also log the request header the passport plugin sents for the URL
|
// We also log the request header the passport plugin sents for the URL
|
||||||
const header = response.req?._header.replace(/Authorization: [^\r\n]*/i, 'Authorization: REDACTED')
|
const header = response.req?._header.replace(/Authorization: [^\r\n]*/i, 'Authorization: REDACTED')
|
||||||
Logger.debug(header + '\n' + response.body?.toString() + '\n' + JSON.stringify(response.body, null, 2))
|
Logger.debug(header + '\n' + JSON.stringify(response.body, null, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const packageJson = require('../../../package.json')
|
const packageJson = require('../../../package.json')
|
||||||
const { BookshelfView } = require('../../utils/constants')
|
const { BookshelfView } = require('../../utils/constants')
|
||||||
const Logger = require('../../Logger')
|
const Logger = require('../../Logger')
|
||||||
|
const User = require('../user/User')
|
||||||
|
|
||||||
class ServerSettings {
|
class ServerSettings {
|
||||||
constructor(settings) {
|
constructor(settings) {
|
||||||
@ -72,6 +73,8 @@ class ServerSettings {
|
|||||||
this.authOpenIDAutoRegister = false
|
this.authOpenIDAutoRegister = false
|
||||||
this.authOpenIDMatchExistingBy = null
|
this.authOpenIDMatchExistingBy = null
|
||||||
this.authOpenIDMobileRedirectURIs = ['audiobookshelf://oauth']
|
this.authOpenIDMobileRedirectURIs = ['audiobookshelf://oauth']
|
||||||
|
this.authOpenIDGroupClaim = ''
|
||||||
|
this.authOpenIDAdvancedPermsClaim = ''
|
||||||
|
|
||||||
if (settings) {
|
if (settings) {
|
||||||
this.construct(settings)
|
this.construct(settings)
|
||||||
@ -129,6 +132,8 @@ class ServerSettings {
|
|||||||
this.authOpenIDAutoRegister = !!settings.authOpenIDAutoRegister
|
this.authOpenIDAutoRegister = !!settings.authOpenIDAutoRegister
|
||||||
this.authOpenIDMatchExistingBy = settings.authOpenIDMatchExistingBy || null
|
this.authOpenIDMatchExistingBy = settings.authOpenIDMatchExistingBy || null
|
||||||
this.authOpenIDMobileRedirectURIs = settings.authOpenIDMobileRedirectURIs || ['audiobookshelf://oauth']
|
this.authOpenIDMobileRedirectURIs = settings.authOpenIDMobileRedirectURIs || ['audiobookshelf://oauth']
|
||||||
|
this.authOpenIDGroupClaim = settings.authOpenIDGroupClaim || ''
|
||||||
|
this.authOpenIDAdvancedPermsClaim = settings.authOpenIDAdvancedPermsClaim || ''
|
||||||
|
|
||||||
if (!Array.isArray(this.authActiveAuthMethods)) {
|
if (!Array.isArray(this.authActiveAuthMethods)) {
|
||||||
this.authActiveAuthMethods = ['local']
|
this.authActiveAuthMethods = ['local']
|
||||||
@ -216,7 +221,9 @@ class ServerSettings {
|
|||||||
authOpenIDAutoLaunch: this.authOpenIDAutoLaunch,
|
authOpenIDAutoLaunch: this.authOpenIDAutoLaunch,
|
||||||
authOpenIDAutoRegister: this.authOpenIDAutoRegister,
|
authOpenIDAutoRegister: this.authOpenIDAutoRegister,
|
||||||
authOpenIDMatchExistingBy: this.authOpenIDMatchExistingBy,
|
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.authOpenIDClientID
|
||||||
delete json.authOpenIDClientSecret
|
delete json.authOpenIDClientSecret
|
||||||
delete json.authOpenIDMobileRedirectURIs
|
delete json.authOpenIDMobileRedirectURIs
|
||||||
|
delete json.authOpenIDGroupClaim
|
||||||
|
delete json.authOpenIDAdvancedPermsClaim
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +271,11 @@ class ServerSettings {
|
|||||||
authOpenIDAutoLaunch: this.authOpenIDAutoLaunch,
|
authOpenIDAutoLaunch: this.authOpenIDAutoLaunch,
|
||||||
authOpenIDAutoRegister: this.authOpenIDAutoRegister,
|
authOpenIDAutoRegister: this.authOpenIDAutoRegister,
|
||||||
authOpenIDMatchExistingBy: this.authOpenIDMatchExistingBy,
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,6 +268,111 @@ class User {
|
|||||||
return hasUpdates
|
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',
|
||||||
|
tagsAreDenylist: 'selectedTagsNotAccessible',
|
||||||
|
// Direct mapping for array-based permissions
|
||||||
|
allowedLibraries: 'librariesAccessible',
|
||||||
|
allowedTags: 'itemTagsSelected'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user permissions from external JSON
|
||||||
|
*
|
||||||
|
* @param {Object} absPermissions JSON containing user permissions
|
||||||
|
* @returns {boolean} true if updates were made
|
||||||
|
*/
|
||||||
|
updatePermissionsFromExternalJSON(absPermissions) {
|
||||||
|
let hasUpdates = false
|
||||||
|
let updatedUserPermissions = {}
|
||||||
|
|
||||||
|
// Initialize all permissions to false first
|
||||||
|
Object.keys(User.permissionMapping).forEach(mappingKey => {
|
||||||
|
const userPermKey = User.permissionMapping[mappingKey]
|
||||||
|
if (typeof this.permissions[userPermKey] === 'boolean') {
|
||||||
|
updatedUserPermissions[userPermKey] = false // Default to false for boolean permissions
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Map the boolean permissions from absPermissions
|
||||||
|
Object.keys(absPermissions).forEach(absKey => {
|
||||||
|
const userPermKey = User.permissionMapping[absKey]
|
||||||
|
if (!userPermKey) {
|
||||||
|
throw new Error(`Unexpected permission property: ${absKey}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedUserPermissions[userPermKey] !== undefined) {
|
||||||
|
updatedUserPermissions[userPermKey] = !!absPermissions[absKey]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update user permissions if changes were made
|
||||||
|
if (JSON.stringify(this.permissions) !== JSON.stringify(updatedUserPermissions)) {
|
||||||
|
this.permissions = updatedUserPermissions
|
||||||
|
hasUpdates = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle allowedLibraries
|
||||||
|
if (this.permissions.accessAllLibraries) {
|
||||||
|
if (this.librariesAccessible.length) {
|
||||||
|
this.librariesAccessible = []
|
||||||
|
hasUpdates = true
|
||||||
|
}
|
||||||
|
} else if (absPermissions.allowedLibraries?.length && absPermissions.allowedLibraries.join(',') !== this.librariesAccessible.join(',')) {
|
||||||
|
if (absPermissions.allowedLibraries.some(lid => typeof lid !== 'string')) {
|
||||||
|
throw new Error('Invalid permission property "allowedLibraries", expecting array of strings')
|
||||||
|
}
|
||||||
|
this.librariesAccessible = absPermissions.allowedLibraries
|
||||||
|
hasUpdates = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle allowedTags
|
||||||
|
if (this.permissions.accessAllTags) {
|
||||||
|
if (this.itemTagsSelected.length) {
|
||||||
|
this.itemTagsSelected = []
|
||||||
|
hasUpdates = true
|
||||||
|
}
|
||||||
|
} else if (absPermissions.allowedTags?.length && absPermissions.allowedTags.join(',') !== this.itemTagsSelected.join(',')) {
|
||||||
|
if (absPermissions.allowedTags.some(tag => typeof tag !== 'string')) {
|
||||||
|
throw new Error('Invalid permission property "allowedTags", expecting array of strings')
|
||||||
|
}
|
||||||
|
this.itemTagsSelected = absPermissions.allowedTags
|
||||||
|
hasUpdates = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasUpdates
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a sample to show how a JSON for updatePermissionsFromExternalJSON should look like
|
||||||
|
*
|
||||||
|
* @returns {string} 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] = [`5406ba8a-16e1-451d-96d7-4931b0a0d966`, `918fd848-7c1d-4a02-818a-847435a879ca`]
|
||||||
|
} 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
|
* Get first available library id for user
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user