mirror of
https://github.com/Lissy93/dashy.git
synced 2024-11-27 10:26:00 +03:00
🔀 Merge pull request #174 from Lissy93/FEATURE/auth-keycloak-support
[FEATURE] Adds Keycloak support Closes #33
This commit is contained in:
commit
fea630f9b9
4
.github/CHANGELOG.md
vendored
4
.github/CHANGELOG.md
vendored
@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## ✨ 1.6.5 - Adds support for Secure Authentication using Keycloak [PR #174](https://github.com/Lissy93/dashy/pull/174)
|
||||
- Major restructure of auth config
|
||||
- Implements keycloak support, adds docs and updates schema
|
||||
|
||||
## ✨ 1.6.4 - Adds functionality for Granular Auth Control [PR #171](https://github.com/Lissy93/dashy/pull/171)
|
||||
- Enables sections to be visible for all users except for those specified
|
||||
- Enables sections to be hidden from all users except for those specified
|
||||
|
24
README.md
24
README.md
@ -64,8 +64,8 @@
|
||||
- Option to show service status for each of your apps / links, for basic availability and uptime monitoring
|
||||
- Choose how to launch apps, either in your browser, a pop-up modal or workspace view
|
||||
- Option for full-screen background image, custom nav-bar links, html footer, title, and more
|
||||
- Encrypted cloud backup and restore feature available
|
||||
- Optional authentication, requiring admins and non-privileged users to log in
|
||||
- Optional encrypted cloud backup and restore feature available
|
||||
- Optional authentication with multi-user support and configurable privileges for protecting your dashboard
|
||||
- Small bundle size, fully responsive UI and PWA makes the app easy to use on any device
|
||||
- Easy to setup with Docker, or on bare metal, or with 1-Click cloud deployment
|
||||
- Multi-language support, with more languages being added regularly
|
||||
@ -243,18 +243,22 @@ You can also specify an time interval in seconds under `appConfig.statusCheckInt
|
||||
|
||||
> For full authentication documentation, see: [**Authentication**](./docs/authentication.md)
|
||||
|
||||
Dashy has a built-in login feature, which can be used for basic access control. To enable this feature, add an `auth` attribute under `appConfig`, containing an array of users, each with a username, SHA-256 hashed password and optional user type.
|
||||
Dashy now has full support for [Keycloak](https://www.keycloak.org/)!
|
||||
|
||||
There is also a simple login feature for basic access control, which doesn't require any additional setup. To enable this feature, add an `auth` attribute under `appConfig`, containing an array of `users`, each with a username, SHA-256 hashed password and optional user type.
|
||||
|
||||
```yaml
|
||||
appConfig:
|
||||
auth:
|
||||
users:
|
||||
- user: alicia
|
||||
hash: 4D1E58C90B3B94BCAD9848ECCACD6D2A8C9FBC5CA913304BBA5CDEAB36FEEFA3
|
||||
type: admin
|
||||
```
|
||||
|
||||
By default, when authentication is configured no user can access your dashboard without first logging in. If you would like to allow for read-only access by unauthenticated users, then you can enable guest mode, by setting `appConfig.enableGuestAccess: true`.
|
||||
**Guest Access**: By default, when authentication is configured no user can access your dashboard without first logging in. If you would like to allow for read-only access by unauthenticated users, then you can enable guest mode, by setting `appConfig.auth.enableGuestAccess: true`.
|
||||
|
||||
**Note**: At present, access control is handled on the frontend, and therefore in security-critical situations, it is recommended to use an alternate method for authentication, such as [Authelia](https://www.authelia.com/), a VPN or web server and firewall rules. Instructions for setting this up can be found [in the docs](docs/authentication.md#alternative-authentication-methods).
|
||||
**Note**: Using the above method involves access control being handled on the frontend, and therefore in security-critical situations, it is recommended to use an alternate method for authentication. Keycloak is [natively supported](docs/authentication.md#keycloak), but you could also use [Authelia](https://www.authelia.com/), a VPN or web server and firewall rules. Instructions for all of these can be found [in the docs](docs/authentication.md#alternative-authentication-methods).
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
@ -265,6 +269,10 @@ By default, when authentication is configured no user can access your dashboard
|
||||
/>
|
||||
</p>
|
||||
|
||||
**Granular Controls**: With basic login, it is also possible to control which sections are visible to which users. Under the `displayData` property of a section, you can pass an array of usernames to one of the following attributes:
|
||||
- `hideForUsers` - Section will be visible to all users, except for those specified in this list
|
||||
- `showForUsers` - Section will be hidden from all users, except for those specified in this list
|
||||
- `hideForGuests` - Section will be visible for all logged in users, but not for guests (if guest access is enabled)
|
||||
|
||||
**[⬆️ Back to Top](#dashy)**
|
||||
|
||||
@ -445,7 +453,7 @@ Several areas that we need a bit of help with at the moment are:
|
||||
- Complete a [short survey](https://n9fy6xak9yd.typeform.com/to/gl0L68ou), to have your say about future features
|
||||
- Share your dashboard in the [Showcase](https://github.com/Lissy93/dashy/blob/master/docs/showcase.md#dashy-showcase-), to provide inspiration for others
|
||||
- Join the [discussion](https://github.com/Lissy93/dashy/discussions), help answer other users questions, suggest features, share tips and ask questions
|
||||
- Spread the word, by sharing Dashy online, to help new users discover it
|
||||
- Spread the word, by sharing Dashy or a screenshot of your dashboard, to help new users discover it
|
||||
- Submit a PR, to add a new feature, fix a bug, update the docs, add a theme or something else
|
||||
- Star Dashy on GitHub/ DockerHub or leave an upvote / review on [these platforms](https://github.com/Lissy93/dashy/blob/master/docs/contributing.md#star-upvote-or-leave-a-review)
|
||||
|
||||
@ -542,7 +550,8 @@ If you're new to web development, I've put together a short [list of resources](
|
||||
## Documentation 📘
|
||||
> For full docs, see: **[Documentation Contents](./docs/readme.md)**
|
||||
#### Running Dashy
|
||||
- 🚀 [Deployment](/docs/deployment.md) - Getting Dashy up and running
|
||||
- 💨 [Quick Start](/docs/quick-start.md) - TDLR guide on getting Dashy up and running in under 5 minutes
|
||||
- 🚀 [Deployment](/docs/deployment.md) - Full guide on setting up Dashy on various different environments
|
||||
- 🔧 [Configuring](/docs/configuring.md) - Complete list of all available options in the config file
|
||||
- 💻 [Management](/docs/management.md) - Managing your app, updating, security, web server configuration, etc
|
||||
- 🚒 [Troubleshooting](/docs/troubleshooting.md) - Common errors and problems, and how to fix them
|
||||
@ -556,6 +565,7 @@ If you're new to web development, I've put together a short [list of resources](
|
||||
|
||||
#### Feature Docs
|
||||
- 🛡️ [Authentication](/docs/authentication.md) - Guide to setting up authentication to protect your dashboard
|
||||
- 🧿 [Alternate Views](/docs/alternate-views.md) - Outline of available pages / views and item opening methods
|
||||
- 💾 [Backup & Restore](/docs/backup-restore.md) - Guide to Dashy's cloud sync feature
|
||||
- 🚦 [Status Indicators](/docs/status-indicators.md) - Using Dashy to monitor uptime and status of your apps
|
||||
- 🧸 [Icons](/docs/icons.md) - Outline of all available icon types for sections and items
|
||||
|
@ -1,10 +1,14 @@
|
||||
# Authentication
|
||||
|
||||
- [Built-In Login Feature](#authentication)
|
||||
- [Basic Auth](#built-in-auth)
|
||||
- [Setting Up Authentication](#setting-up-authentication)
|
||||
- [Hash Password](#hash-password)
|
||||
- [Logging In and Out](#logging-in-and-out)
|
||||
- [Security](#security)
|
||||
- [Keycloak Auth](#keycloak)
|
||||
- [Deploying Keycloak](#1-deploy-keycloak)
|
||||
- [Setting up Keycloak](#2-setup-keycloak-users)
|
||||
- [Configuring Dashy for Keycloak](#3-enable-keycloak-in-dashy-config-file)
|
||||
- [Alternative Authentication Methods](#alternative-authentication-methods)
|
||||
- [VPN](#vpn)
|
||||
- [IP-Based Access](#ip-based-access)
|
||||
@ -12,34 +16,36 @@
|
||||
- [OAuth Services](#oauth-services)
|
||||
- [Auth on Cloud Hosting Services](#static-site-hosting-providers)
|
||||
|
||||
## Built-In Auth
|
||||
Dashy has a basic login page included, and frontend authentication. You can enable this by adding users to the `auth` section under `appConfig` in your `conf.yml`. If this section is not specified, then no authentication will be required to access the app, and it the homepage will resolve to your dashboard.
|
||||
|
||||
## Setting Up Authentication
|
||||
### Setting Up Authentication
|
||||
The `auth` property takes an array of users. Each user needs to include a username, hash and optional user type (`admin` or `normal`). The hash property is a [SHA-256 Hash](https://en.wikipedia.org/wiki/SHA-2) of your desired password.
|
||||
|
||||
For example:
|
||||
```yaml
|
||||
appConfig:
|
||||
auth:
|
||||
users:
|
||||
- user: alicia
|
||||
hash: 4D1E58C90B3B94BCAD9848ECCACD6D2A8C9FBC5CA913304BBA5CDEAB36FEEFA3
|
||||
type: admin
|
||||
- user: edward
|
||||
- user: bob
|
||||
hash: 5E884898DA28047151D0E56F8DC6292773603D0D6AABBDD62A11EF721D1542D8
|
||||
type: admin
|
||||
```
|
||||
## Hash Password
|
||||
|
||||
### Hash Password
|
||||
Dashy uses [SHA-256 Hash](https://en.wikipedia.org/wiki/Sha-256), a 64-character string, which you can generate using an online tool, such as [this one](https://passwordsgenerator.net/sha256-hash-generator/) or [CyberChef](https://gchq.github.io/CyberChef/) (which can be self-hosted/ ran locally).
|
||||
|
||||
A hash is a one-way cryptographic function, meaning that it is easy to generate a hash for a given password, but very hard to determine the original password for a given hash. This means, that so long as your password is long, strong and unique, it is safe to store it's hash in the clear. Having said that, you should never reuse passwords, hashes can be cracked by iterating over known password lists, generating a hash of each.
|
||||
|
||||
## Logging In and Out
|
||||
### Logging In and Out
|
||||
Once authentication is enabled, so long as there is no valid token in cookie storage, the application will redirect the user to the login page. When the user enters credentials in the login page, they will be checked, and if valid, then a token will be generated, and they can be redirected to the home page. If credentials are invalid, then an error message will be shown, and they will remain on the login page. Once in the application, to log out the user can click the logout button (in the top-right), which will clear cookie storage, causing them to be redirected back to the login page.
|
||||
|
||||
## Enabling Guest Access
|
||||
### Enabling Guest Access
|
||||
With authentication setup, by default no access is allowed to your dashboard without first logging in with valid credentials. Guest mode can be enabled to allow for read-only access to a secured dashboard by any user, without the need to log in. A guest user cannot write any changes to the config file, but can apply modifications locally (stored in their browser). You can enable guest access, by setting `appConfig.enableGuestAccess: true`.
|
||||
|
||||
## Granular Access
|
||||
### Granular Access
|
||||
You can use the following properties to make certain sections only visible to some users, or hide sections from guests.
|
||||
- `hideForUsers` - Section will be visible to all users, except for those specified in this list
|
||||
- `showForUsers` - Section will be hidden from all users, except for those specified in this list
|
||||
@ -66,21 +72,84 @@ For Example:
|
||||
...
|
||||
```
|
||||
|
||||
## Security
|
||||
### Security
|
||||
Since all authentication is happening entirely on the client-side, it is vulnerable to manipulation by an adversary. An attacker could look at the source code, find the function used generate the auth token, then decode the minified JavaScript to find the hash, and manually generate a token using it, then just insert that value as a cookie using the console, and become a logged in user. Therefore, if you need secure authentication for your app, it is strongly recommended to implement this using your web server, or use a VPN to control access to Dashy. The purpose of the login page is merely to prevent immediate unauthorized access to your homepage.
|
||||
|
||||
Addressing this is on the todo list, and there are several potential solutions:
|
||||
1. Encrypt all site data against the users password, so that an attacker can not physically access any data without the correct decryption key
|
||||
2. Use a backend service to handle authentication and configuration, with no user data returned from the server until the correct credentials are provided. However, this would require either Dashy to be run using it's Node.js server, or the use of an external service
|
||||
3. Implement authentication using a self-hosted identity management solution, such as [Keycloak for Vue](https://www.keycloak.org/securing-apps/vue)
|
||||
3. ~~Implement authentication using a self-hosted identity management solution, such as [Keycloak for Vue](https://www.keycloak.org/securing-apps/vue)~~ **This is now implemented, and released in PR #174 of V 1.6.5!**
|
||||
|
||||
**[⬆️ Back to Top](#authentication)**
|
||||
|
||||
---
|
||||
|
||||
## Keycloak
|
||||
|
||||
Dashy also supports using a [Keycloack](https://www.keycloak.org/) authentication server. The setup for this is a bit more involved, but it gives you greater security overall, useful for if your instance is exposed to the internet.
|
||||
|
||||
[Keycloak](https://www.keycloak.org/about.html) is a Java-based [open source](https://github.com/keycloak/keycloak), high-performance, secure authentication system, supported by [RedHad](https://www.redhat.com/en). It is easy to setup ([with Docker](https://quay.io/repository/keycloak/keycloak)), and enables you to secure multiple self-hosted applications with single-sign on using standard protocols (OpenID Connect, OAuth 2.0, SAML 2.0 and social login). It's also very customizable, you can write or use custom [themes](https://wjw465150.gitbooks.io/keycloak-documentation/content/server_development/topics/themes.html), [plugins](https://www.keycloak.org/extensions.html), [password policies](https://wjw465150.gitbooks.io/keycloak-documentation/content/server_admin/topics/authentication/password-policies.html) and more.
|
||||
The following guide will walk you through setting up Keycloak with Dashy. If you already have a Keycloak instance configured, then skip to Step 3.
|
||||
|
||||
### 1. Deploy Keycloak
|
||||
|
||||
First thing to do is to spin up a new instance of Keycloak. You will need [Docker installed](https://docs.docker.com/engine/install/), and can then choose a tag, and pull the container from [quay.io/keycloak/keycloak](https://quay.io/repository/keycloak/keycloak)
|
||||
|
||||
Use the following run command, replacing the attributes (default credentials, port and name), or incorporate this into your docker-compose file.
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 8081:8080 \
|
||||
--name auth-server \
|
||||
-e KEYCLOAK_USER=admin \
|
||||
-e KEYCLOAK_PASSWORD=admin \
|
||||
quay.io/keycloak/keycloak:15.0.2
|
||||
```
|
||||
|
||||
If you need to pull from DockerHub, a non-official image is available [here](https://registry.hub.docker.com/r/jboss/keycloak). Or if you would prefer not to use Docker, you can also directly install Keycloak from source, following [this guide](https://www.keycloak.org/docs/latest/getting_started/index.html).
|
||||
|
||||
You should now be able to access the Keycloak web interface, using the port specified above (e.g. `http://127.0.0.1:8081`), login with the default credentials, and when prompted create a new password.
|
||||
|
||||
### 2. Setup Keycloak Users
|
||||
|
||||
Before we can use Keycloak, we must first set it up with some users. Keycloak uses Realms (similar to tenants) to create isolated groups of users. You must create a Realm before you will be able to add your first user.
|
||||
1. Head over to the admin console
|
||||
2. In the top-left corner there is a dropdown called 'Master', hover over it and then click 'Add Realm'
|
||||
3. Give your realm a name, and hit 'Create'
|
||||
|
||||
You can now create your first user.
|
||||
1. In the left-hand menu, click 'Users', then 'Add User'
|
||||
2. Fill in the form, including username and hit 'Save'
|
||||
3. Under the 'Credentials' tab, give the new user an initial password. They will be prompted to change this after first login
|
||||
|
||||
The last thing we need to do in the Keycloak admin console is to create a new client
|
||||
1. Within your new realm, navigate to 'Clients' on the left-hand side, then click 'Create' in the top-right
|
||||
2. Choose a 'Client ID', set 'Client Protocol' to 'openid-connect', and for 'Valid Redirect URIs' put a URL pattern to where you're hosting Dashy (if you're just testing locally, then * is fine), and do the same for the 'Web Origins' field
|
||||
3. Make note of your client-id, and click 'Save'
|
||||
|
||||
### 3. Enable Keycloak in Dashy Config File
|
||||
Now that your Keycloak instance is up and running, all that's left to do is to configure Dashy to use it. Under `appConfig`, set `auth.enableKeycloak: true`, then fill in the details in `auth.keycloak`, including: `serverUrl` - the URL where your Keycloak instance is hosted, `realm` - the name you gave your Realm, and `clientId` - the Client ID you chose.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
appConfig:
|
||||
...
|
||||
auth:
|
||||
enableKeycloak: true
|
||||
keycloak:
|
||||
serverUrl: 'http://localhost:8081'
|
||||
realm: 'alicia-homelab'
|
||||
clientId: 'dashy'
|
||||
```
|
||||
Your app is now secured :) When you load Dashy, it will redirect to your Keycloak login page, and any user without valid credentials will be prevented from accessing your dashboard.
|
||||
|
||||
From within the Keycloak console, you can then configure things like user permissions, time outs, password policies, access, etc. You can also backup your full Keycloak config, and it is recommended to do this, along with your Dashy config. You can spin up both Dashy and Keycloak simultaneously and restore both applications configs using a `docker-compose.yml` file, and this is recommended.
|
||||
|
||||
---
|
||||
|
||||
## Alternative Authentication Methods
|
||||
|
||||
If you are self-hosting Dashy, and require secure authentication to prevent unauthorized access, you have several options:
|
||||
If you are self-hosting Dashy, and require secure authentication to prevent unauthorized access, then you can either use Keycloak, or one of the following options:
|
||||
- [Authentication Server](#authentication-server) - Put Dashy behind a self-hosted auth server
|
||||
- [VPN](#vpn) - Use a VPN to tunnel into the network where Dashy is running
|
||||
- [IP-Based Access](#ip-based-access) - Disallow access from all IP addresses, except your own
|
||||
|
@ -64,8 +64,7 @@ To disallow any changes from being written to disk via the UI config editor, set
|
||||
**`enableFontAwesome`** | `boolean` | _Optional_ | Where `true` is enabled, if left blank font-awesome will be enabled only if required by 1 or more icons
|
||||
**`fontAwesomeKey`** | `string` | _Optional_ | If you have a font-awesome key, then you can use it here and make use of premium icons. It is a 10-digit alpha-numeric string from you're FA kit URL (e.g. `13014ae648`)
|
||||
**`faviconApi`** | `enum` | _Optional_ | Only applicable if you are using favicons for item icons. Specifies which service to use to resolve favicons. Set to `local` to do this locally, without using an API. Services running locally will use this option always. Available options are: `local`, `faviconkit`, `google`, `clearbit`, `webmasterapi` and `allesedv`. Defaults to `faviconkit`. See [Icons](/docs/icons.md#favicons) for more info
|
||||
**`auth`** | `array` | _Optional_ | An array of objects containing usernames and hashed passwords. If this is not provided, then authentication will be off by default, and you will not need any credentials to access the app. Note authentication is done on the client side, and so if your instance of Dashy is exposed to the internet, it is recommend to configure your web server to handle this. See [`auth`](#appconfigauth-optional)
|
||||
**`enableGuestAccess`** | `boolean` | _Optional_ | When set to `true`, an unauthenticated user will be able to access the dashboard, with read-only access, without having to login. Requires `auth` to be configured. Defaults to `false`.
|
||||
**`auth`** | `object` | _Optional_ | All settings relating to user authentication. See [`auth`](#appconfigauth-optional)
|
||||
**`layout`** | `enum` | _Optional_ | App layout, either `horizontal`, `vertical`, `auto` or `sidebar`. Defaults to `auto`. This specifies the layout and direction of how sections are positioned on the home screen. This can also be modified from the UI.
|
||||
**`iconSize`** | `enum` | _Optional_ | The size of link items / icons. Can be either `small`, `medium,` or `large`. Defaults to `medium`. This can also be set directly from the UI.
|
||||
**`theme`** | `string` | _Optional_ | The default theme for first load (you can change this later from the UI)
|
||||
@ -85,6 +84,18 @@ To disallow any changes from being written to disk via the UI config editor, set
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
### `appConfig.auth` _(optional)_
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
**`users`** | `array` | _Optional_ | An array of objects containing usernames and hashed passwords. If this is not provided, then authentication will be off by default, and you will not need any credentials to access the app. See [`appConfig.auth.users`](#appconfigauthusers-optional). <br>**Note** this method of authentication is handled on the client side, so for security critical situations, it is recommended to use an [alternate authentication method](/docs/authentication.md#alternative-authentication-methods).
|
||||
**`enableKeycloak`** | `object` | _Optional_ | If set to `true`, then authentication using Keycloak will be anabled. Note that you need to have an instance running, and have also configured `auth.keycloak`. Defaults to `false`
|
||||
**`keycloak`** | `boolean` | _Optional_ | Config options to point Dashy to your Keycloak server. Requires `enableKeycloak: true`. See [`auth.keycloak`](#appconfigauthkeycloak-optional) for more info
|
||||
**`enableGuestAccess`** | `boolean` | _Optional_ | When set to `true`, an unauthenticated user will be able to access the dashboard, with read-only access, without having to login. Requires `auth.users` to be configured. Defaults to `false`.
|
||||
|
||||
For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
### `appConfig.auth.users` _(optional)_
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
@ -94,6 +105,17 @@ To disallow any changes from being written to disk via the UI config editor, set
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
### `appConfig.auth.keycloak` _(optional)_
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
**`serverUrl`** | `string` | Required | The URL (or URL/ IP + Port) where your keycloak server is running
|
||||
**`realm`** | `string` | Required | The name of the realm (must already be created) that you want to use
|
||||
**`clientId`** | `string` | Required | The Client ID of the client you created for use with Dashy
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
|
||||
### `appConfig.hideComponents` _(optional)_
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
|
@ -91,5 +91,22 @@ If the issue still persists, you should raise an issue.
|
||||
|
||||
---
|
||||
|
||||
## Node Sass does not yet support your current environment
|
||||
Caused by node-sass's binaries being built for a for a different architecture
|
||||
To fix this, just run: `yarn rebuild node-sass`
|
||||
|
||||
---
|
||||
|
||||
## Error: Cannot find module './_baseValues'
|
||||
Clearing the cache should fix this: `yarn cache clean`
|
||||
If the issue persists, remove (`rm -rf node_modules\ yarn.lock`) and reinstall (`yarn`) node_modules
|
||||
|
||||
---
|
||||
|
||||
## Invalid Host Header while running through ngrok
|
||||
Just add the [-host-header](https://ngrok.com/docs#http-host-header) flag, e.g. `ngrok http 8080 -host-header="localhost:8080"`
|
||||
|
||||
---
|
||||
|
||||
## Warnings in the Console during deploy
|
||||
Please acknowledge the difference between errors and warnings before raising an issue about messages in the console. It's not unusual to see warnings about a new version of a certain package being available, an asset bundle bing oversized or a service worker not yet having a cache. These shouldn't have any impact on the running application, so please don't raise issues about these unless it directly relates to a bug or issue you're experiencing. Errors on the other hand should not appear in the console, and they are worth looking into further.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Dashy",
|
||||
"version": "1.6.4",
|
||||
"version": "1.6.5",
|
||||
"license": "MIT",
|
||||
"main": "server",
|
||||
"scripts": {
|
||||
@ -24,6 +24,7 @@
|
||||
"connect": "^3.7.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"keycloak-js": "^15.0.2",
|
||||
"register-service-worker": "^1.6.2",
|
||||
"remedial": "^1.0.8",
|
||||
"serve-static": "^1.14.1",
|
||||
|
23
src/main.js
23
src/main.js
@ -2,6 +2,7 @@
|
||||
// Import core framework and essential utils
|
||||
import Vue from 'vue';
|
||||
import VueI18n from 'vue-i18n'; // i18n for localization
|
||||
import Keycloak from 'keycloak-js';
|
||||
|
||||
// Import component Vue plugins, used throughout the app
|
||||
import VTooltip from 'v-tooltip'; // A Vue directive for Popper.js, tooltip component
|
||||
@ -18,6 +19,7 @@ import clickOutside from '@/utils/ClickOutside'; // Directive for closing p
|
||||
import { messages } from '@/utils/languages'; // Language texts
|
||||
import ErrorReporting from '@/utils/ErrorReporting'; // Error reporting initializer (off)
|
||||
import { toastedOptions, language as defaultLanguage } from '@/utils/defaults'; // Defaults
|
||||
import { isKeycloakEnabled, getKeycloakConfig } from '@/utils/Auth'; // Keycloak auth config
|
||||
|
||||
// Initialize global Vue components
|
||||
Vue.use(VueI18n);
|
||||
@ -46,5 +48,22 @@ ErrorReporting(Vue, router);
|
||||
// Render function
|
||||
const render = (awesome) => awesome(Dashy);
|
||||
|
||||
// All done, now just initialize main Vue app!
|
||||
new Vue({ router, render, i18n }).$mount('#app');
|
||||
// If Keycloak not enabled, then proceed straight to the app
|
||||
if (!isKeycloakEnabled()) {
|
||||
new Vue({ router, render, i18n }).$mount('#app');
|
||||
} else { // Keycloak is enabled, redirect to KC login page
|
||||
const { serverUrl, realm, clientId } = getKeycloakConfig();
|
||||
const initOptions = {
|
||||
url: `${serverUrl}/auth`, realm, clientId, onLoad: 'login-required',
|
||||
};
|
||||
const keycloak = Keycloak(initOptions);
|
||||
keycloak.init({ onLoad: initOptions.onLoad }).then((auth) => {
|
||||
if (!auth) {
|
||||
// Not authenticated, reload to Keycloak login page
|
||||
window.location.reload();
|
||||
} else {
|
||||
// Yay - user successfully authenticated with Keycloak, render the app!
|
||||
new Vue({ router, render, i18n }).$mount('#app');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -16,22 +16,18 @@ import Minimal from '@/views/Minimal.vue';
|
||||
import DownloadConfig from '@/views/DownloadConfig.vue';
|
||||
|
||||
// Import helper functions, config data and defaults
|
||||
import { isLoggedIn } from '@/utils/Auth';
|
||||
import { isAuthEnabled, isLoggedIn, isGuestAccessEnabled } from '@/utils/Auth';
|
||||
import { config } from '@/utils/ConfigHelpers';
|
||||
import { metaTagData, startingView, routePaths } from '@/utils/defaults';
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
/* Checks if guest mode is enabled in appConfig */
|
||||
const isGuestEnabled = () => {
|
||||
if (!config || !config.appConfig) return false;
|
||||
return config.appConfig.enableGuestAccess || false;
|
||||
};
|
||||
|
||||
/* Returns true if user is already authenticated, or if auth is not enabled */
|
||||
const isAuthenticated = () => {
|
||||
const users = config.appConfig.auth;
|
||||
return (!users || users.length === 0 || isLoggedIn() || isGuestEnabled());
|
||||
const authEnabled = isAuthEnabled();
|
||||
const userLoggedIn = isLoggedIn();
|
||||
const guestEnabled = isGuestAccessEnabled();
|
||||
return (!authEnabled || userLoggedIn || guestEnabled);
|
||||
};
|
||||
|
||||
/* Get the users chosen starting view from app config, or return default */
|
||||
@ -97,7 +93,7 @@ const router = new Router({
|
||||
},
|
||||
beforeEnter: (to, from, next) => {
|
||||
// If the user already logged in + guest mode not enabled, then redirect home
|
||||
if (isAuthenticated() && !isGuestEnabled()) router.push({ path: '/' });
|
||||
if (isAuthenticated() && !isGuestAccessEnabled()) router.push({ path: '/' });
|
||||
next();
|
||||
},
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
import sha256 from 'crypto-js/sha256';
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { cookieKeys, localStorageKeys, userStateEnum } from '@/utils/defaults';
|
||||
|
||||
/* Uses config accumulator to get and return app config */
|
||||
@ -9,10 +10,48 @@ const getAppConfig = () => {
|
||||
return config.appConfig || {};
|
||||
};
|
||||
|
||||
/* Returns the users array from appConfig, if available, else an empty array */
|
||||
/**
|
||||
* Called when the user is still using array for users, prints warning
|
||||
* This was a breaking change, implemented in V 1.6.5
|
||||
* Support for old user structure will be removed in V 1.7.0
|
||||
*/
|
||||
const printWarning = () => {
|
||||
const msg = 'From V 1.6.5 onwards, the structure of the users object has changed.';
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(msg);
|
||||
};
|
||||
|
||||
/* Returns true if keycloak is enabled */
|
||||
export const isKeycloakEnabled = () => {
|
||||
const appConfig = getAppConfig();
|
||||
if (!appConfig.auth) return false;
|
||||
return appConfig.auth.enableKeycloak || false;
|
||||
};
|
||||
|
||||
/* Returns the users keycloak config */
|
||||
export const getKeycloakConfig = () => {
|
||||
const appConfig = getAppConfig();
|
||||
if (!isKeycloakEnabled()) return false;
|
||||
const { keycloak } = appConfig.auth;
|
||||
const { serverUrl, realm, clientId } = keycloak;
|
||||
if (!serverUrl || !realm || !clientId) {
|
||||
ErrorHandler('Keycloak config missing- please ensure you specify: serverUrl, realm, clientId');
|
||||
return false;
|
||||
}
|
||||
return keycloak;
|
||||
};
|
||||
|
||||
/* Returns array of users from appConfig.auth, if available, else an empty array */
|
||||
const getUsers = () => {
|
||||
const appConfig = getAppConfig();
|
||||
return appConfig.auth || [];
|
||||
const auth = appConfig.auth || {};
|
||||
// Check if the user is still using previous schema type
|
||||
if (Array.isArray(auth)) {
|
||||
printWarning(); // Print warning message
|
||||
return auth; // Let the user proceed anyway, will remove in V 1.7.0
|
||||
}
|
||||
// Otherwise, return the users array, if available
|
||||
return auth.users || [];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -58,7 +97,15 @@ export const isAuthEnabled = () => {
|
||||
/* Returns true if guest access is enabled */
|
||||
export const isGuestAccessEnabled = () => {
|
||||
const appConfig = getAppConfig();
|
||||
return appConfig.enableGuestAccess || false;
|
||||
if (appConfig.enableGuestAccess) {
|
||||
// User is still using the old auth method
|
||||
printWarning();
|
||||
return true;
|
||||
}
|
||||
if (appConfig.auth && !Array.isArray(appConfig.auth)) {
|
||||
return appConfig.auth.enableGuestAccess || false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "Path to an optional image asset, to be displayed in the header",
|
||||
"pattern": "^(http|\/)",
|
||||
"pattern": "^(http|/)",
|
||||
"examples": [
|
||||
"/web-icons/dashy-logo.png",
|
||||
"https://i.ibb.co/yhbt6CY/dashy.png"
|
||||
@ -217,42 +217,78 @@
|
||||
"description": "How often to recheck statuses. If set to 0, status will only be checked on page load"
|
||||
},
|
||||
"auth": {
|
||||
"type": "array",
|
||||
"description": "Usernames and hashed credentials for frontend authentication",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"user",
|
||||
"hash"
|
||||
],
|
||||
"properties": {
|
||||
"user": {
|
||||
"type": "string",
|
||||
"description": "The username for a user"
|
||||
},
|
||||
"hash": {
|
||||
"type": "string",
|
||||
"description": "A SHA-256 hashed password for that user",
|
||||
"minLength": 64,
|
||||
"maxLength": 64
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"admin",
|
||||
"normal"
|
||||
"type": "object",
|
||||
"description": "Settings for enabling authentication",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enableGuestAccess": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If set to true, an unauthenticated user will be able to have read-only access to dashboard, without needing to login. Requires auth to be configured."
|
||||
},
|
||||
"users": {
|
||||
"type": "array",
|
||||
"description": "Usernames and hashed credentials for frontend authentication",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"user",
|
||||
"hash"
|
||||
],
|
||||
"description": "User type, denoting privilege level, either admin or normal",
|
||||
"default": "normal"
|
||||
"properties": {
|
||||
"user": {
|
||||
"type": "string",
|
||||
"description": "The username for a user"
|
||||
},
|
||||
"hash": {
|
||||
"type": "string",
|
||||
"description": "A SHA-256 hashed password for that user",
|
||||
"minLength": 64,
|
||||
"maxLength": 64
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"admin",
|
||||
"normal"
|
||||
],
|
||||
"description": "User type, denoting privilege level, either admin or normal",
|
||||
"default": "normal"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"enableKeycloak": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If set to true, and auth.keycloak is also configured, then Keycloak will be used for app auth"
|
||||
},
|
||||
"keycloak": {
|
||||
"type": "object",
|
||||
"description": "Configuration for Keycloak server",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"serverUrl",
|
||||
"realm",
|
||||
"clientId"
|
||||
],
|
||||
"properties": {
|
||||
"serverUrl": {
|
||||
"type": "string",
|
||||
"description": "The URL (or URL/ IP + Port) where your keycloak server is running"
|
||||
},
|
||||
"realm": {
|
||||
"type": "string",
|
||||
"description": "The name of the realm (must already be created) that you want to use"
|
||||
},
|
||||
"clientId": {
|
||||
"type": "string",
|
||||
"description": "The Client ID of the client you created for use with Dashy"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"enableGuestAccess": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If set to true, an unauthenticated user will be able to have read-only access to dashboard, without needing to login. Requires auth to be configured."
|
||||
},
|
||||
"enableMultiTasking": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
|
@ -49,7 +49,7 @@
|
||||
</form>
|
||||
<!-- Guest login form -->
|
||||
<form class="guest-form"
|
||||
v-if="appConfig.enableGuestAccess && !isUserAlreadyLoggedIn && isAuthenticationEnabled">
|
||||
v-if="isGuestAccessEnabled && !isUserAlreadyLoggedIn && isAuthenticationEnabled">
|
||||
<h2 class="login-title">Guest Access</h2>
|
||||
<Button class="login-button" :click="guestLogin">
|
||||
{{ $t('login.proceed-guest-button') }}
|
||||
@ -81,6 +81,7 @@ import {
|
||||
login,
|
||||
isLoggedIn,
|
||||
logout,
|
||||
isGuestAccessEnabled,
|
||||
} from '@/utils/Auth';
|
||||
|
||||
export default {
|
||||
@ -124,17 +125,19 @@ export default {
|
||||
existingUsername() {
|
||||
return localStorage[localStorageKeys.USERNAME];
|
||||
},
|
||||
users() {
|
||||
const auth = this.appConfig.auth || {};
|
||||
return Array.isArray(auth) ? auth : auth.users || [];
|
||||
},
|
||||
isUserAlreadyLoggedIn() {
|
||||
const users = this.appConfig.auth;
|
||||
const loggedIn = (!users || users.length === 0 || isLoggedIn());
|
||||
const loggedIn = (!this.users || this.users.length === 0 || isLoggedIn());
|
||||
return (loggedIn && this.existingUsername);
|
||||
},
|
||||
isGuestAccessEnabled() {
|
||||
if (!this.appConfig || !this.appConfig.enableGuestAccess) return false;
|
||||
return this.appConfig.enableGuestAccess;
|
||||
return isGuestAccessEnabled();
|
||||
},
|
||||
isAuthenticationEnabled() {
|
||||
return (this.appConfig && this.appConfig.auth && this.appConfig.auth.length > 0);
|
||||
return (this.appConfig && this.appConfig.auth && this.users.length > 0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -146,7 +149,7 @@ export default {
|
||||
const response = checkCredentials(
|
||||
this.username,
|
||||
this.password,
|
||||
this.appConfig.auth || [], // All users
|
||||
this.users, // All users
|
||||
this.responseMessages, // Translated response messages
|
||||
);
|
||||
this.message = response.msg; // Show error or success message to the user
|
||||
|
18
yarn.lock
18
yarn.lock
@ -2286,6 +2286,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
@ -5845,6 +5850,11 @@ js-queue@2.0.2:
|
||||
dependencies:
|
||||
easy-stack "^1.0.1"
|
||||
|
||||
js-sha256@0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
|
||||
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -6003,6 +6013,14 @@ jsprim@^1.2.2:
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
keycloak-js@^15.0.2:
|
||||
version "15.0.2"
|
||||
resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-15.0.2.tgz#9d12dd8860953a267b9b18f351ad2e76b8e94a9c"
|
||||
integrity sha512-dv2a4NcPSH3AzGWG3ZtB+VrHpuQLdFBYXtQBj/+oBzm6XNwnVAMdL6LIC0OzCLQpn3rKTQJtNSATAGhbKJgewQ==
|
||||
dependencies:
|
||||
base64-js "1.3.1"
|
||||
js-sha256 "0.9.0"
|
||||
|
||||
keyv@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
|
||||
|
Loading…
Reference in New Issue
Block a user