diff --git a/docs/authentication.md b/docs/authentication.md index 5147d9b3..35846b1a 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -286,6 +286,7 @@ appConfig: clientId: [registered client id] endpoint: [OIDC endpoint] scope: [The scope(s) to request from the OIDC provider] + adminGroup: admin ``` Because Dashy is a SPA, a [public client](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) registration with PKCE is needed. diff --git a/docs/configuring.md b/docs/configuring.md index a058d072..62b876f7 100644 --- a/docs/configuring.md +++ b/docs/configuring.md @@ -204,6 +204,8 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)** --- | --- | --- | --- **`clientId`** | `string` | Required | The client id registered in the OIDC server **`endpoint`** | `string` | Required | The URL of the OIDC server that should be used. +**`adminRole`** | `string` | _Optional_ | The role that will be considered as admin. +**`adminGroup`** | `string` | _Optional_ | The group that will be considered as admin. **`scope`** | `string` | Required | The scope(s) to request from the OIDC provider **[⬆️ Back to Top](#configuring)** diff --git a/src/utils/Auth.js b/src/utils/Auth.js index 076b99d1..dd2ea5eb 100644 --- a/src/utils/Auth.js +++ b/src/utils/Auth.js @@ -31,7 +31,19 @@ const getUsers = () => { return []; // Support for old data structure now removed } // Otherwise, return the users array, if available - return auth.users || []; + + const users = auth.users || []; + if (isOidcEnabled()) { + if (localStorage[localStorageKeys.USERNAME]) { + const user = { + user: localStorage[localStorageKeys.USERNAME], + type: localStorage[localStorageKeys.ISADMIN] === 'true' ? 'admin' : 'normal', + }; + users.push(user); + } + } + + return users; }; /** @@ -80,6 +92,17 @@ export const makeBasicAuthHeaders = () => { export const isLoggedIn = () => { const users = getUsers(); const cookieToken = getCookieToken(); + + if (isOidcEnabled()) { + const username = localStorage[localStorageKeys.USERNAME]; // Get username + if (!username) return false; // No username + return users.some((user) => { + if (user.user === username || generateUserToken(user) === cookieToken) { + return true; + } else return false; + }); + } + return users.some((user) => { if (generateUserToken(user) === cookieToken) { localStorage.setItem(localStorageKeys.USERNAME, user.user); diff --git a/src/utils/ConfigSchema.json b/src/utils/ConfigSchema.json index 87a11840..92039798 100644 --- a/src/utils/ConfigSchema.json +++ b/src/utils/ConfigSchema.json @@ -566,6 +566,18 @@ "type": "string", "description": "ClientId from OIDC provider" }, + "adminRole" : { + "title": "Admin Role", + "type": "string", + "default": false, + "description": "The role that will be considered as admin. If not set, no roles will be considered as admin" + }, + "adminGroup" : { + "title": "Admin Group", + "type": "string", + "default": false, + "description": "The group that will be considered as admin. If not set, no groups will be considered as admin" + }, "scope" : { "title": "OIDC Scope", "type": "string", diff --git a/src/utils/OidcAuth.js b/src/utils/OidcAuth.js index 5d438404..675d83c1 100644 --- a/src/utils/OidcAuth.js +++ b/src/utils/OidcAuth.js @@ -13,7 +13,13 @@ const getAppConfig = () => { class OidcAuth { constructor() { const { auth } = getAppConfig(); - const { clientId, endpoint, scope } = auth.oidc; + const { + clientId, + endpoint, + scope, + adminGroup, + adminRole, + } = auth.oidc; const settings = { userStore: new WebStorageStateStore({ store: window.localStorage }), authority: endpoint, @@ -25,6 +31,8 @@ class OidcAuth { filterProtocolClaims: true, }; + this.adminGroup = adminGroup; + this.adminRole = adminRole; this.userManager = new UserManager(settings); } @@ -43,22 +51,27 @@ class OidcAuth { if (user === null) { await this.userManager.signinRedirect(); } else { - const { roles, groups } = user.profile; + const { roles = [], groups = [] } = user.profile; const info = { groups, roles, }; + const isAdmin = (Array.isArray(groups) && groups.includes(this.adminGroup)) + || (Array.isArray(roles) && roles.includes(this.adminRole)) + || false; - statusMsg(`user: ${user.profile.preferred_username}`, JSON.stringify(info)); + statusMsg(`user: ${user.profile.preferred_username} admin: ${isAdmin}`, JSON.stringify(info)); localStorage.setItem(localStorageKeys.KEYCLOAK_INFO, JSON.stringify(info)); localStorage.setItem(localStorageKeys.USERNAME, user.profile.preferred_username); + localStorage.setItem(localStorageKeys.ISADMIN, isAdmin); } } async logout() { localStorage.removeItem(localStorageKeys.USERNAME); localStorage.removeItem(localStorageKeys.KEYCLOAK_INFO); + localStorage.removeItem(localStorageKeys.ISADMIN); try { await this.userManager.signoutRedirect(); diff --git a/src/utils/defaults.js b/src/utils/defaults.js index 2b690c0b..12764ebe 100644 --- a/src/utils/defaults.js +++ b/src/utils/defaults.js @@ -137,6 +137,7 @@ module.exports = { MOST_USED: 'mostUsed', LAST_USED: 'lastUsed', KEYCLOAK_INFO: 'keycloakInfo', + ISADMIN: 'isAdmin', DISABLE_CRITICAL_WARNING: 'disableCriticalWarning', }, /* Key names for cookie identifiers */