Skip to content

Commit

Permalink
feat(ui): authentication (#591)
Browse files Browse the repository at this point in the history
* feat: credentials manager

* feat: credentials apis

* fix: startup errors

* feat(ui): login page

* fix: authentication working

* feat: use jwt with socketio

* fix: authentication management on refresh

* fix: lint errors

* fix: other lint errors

* feat: password update dialog

* fix: lint errors

* chore: CSRF protection

* fix: disable csfr for now

* fix: remove credential store, use json store instead and hash/salt user psw

* fix: user password localstorage

* fix: don't return user not found

* fix: typo

* fix: logout failed error

* fix: removed credentials key and file from config

* fix: use passwordHash prop for users

* feat: pluggable settings

* fix: login/logout when switching mode

* fix: typo

* fix: docs and error when session secret is not changed

* fix: csfr

* fix: lint issues

* fix: USE_SECURE_COOKIE env var

* fix: add some more rate limiters

* fix: lint errors

* fix: added missing rate limiter

* fix: use path.normalize to make path secure

* fix: typo

* fix: lint issues

* fix: don't return success on auth if session is present

* fix: lint issues

* docs: add auth

* fix(ui): login ui

* fix(ui): toggle password visibility

* fix(ui): don't directly use store vars in settings

* fic(ui): show error and allow retry when checkAuth fails
  • Loading branch information
robertsLando authored Feb 18, 2021
1 parent fa6eeca commit 7eef6c5
Show file tree
Hide file tree
Showing 19 changed files with 1,614 additions and 331 deletions.
350 changes: 321 additions & 29 deletions app.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions config/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ const { joinPath } = require('../lib/utils')
module.exports = {
title: 'ZWave To MQTT',
storeDir: process.env.STORE_DIR || joinPath(true, 'store'),
defaultUser: 'admin',
defaultPsw: 'zwave',
sessionSecret:
process.env.SESSION_SECRET || 'DEFAULT_SESSION_SECRET_CHANGE_ME',
base: '/',
port: 8091
}
3 changes: 2 additions & 1 deletion config/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
module.exports = {
settings: { file: 'settings.json', default: {} },
scenes: { file: 'scenes.json', default: [] },
nodes: { file: 'nodes.json', default: [] }
nodes: { file: 'nodes.json', default: [] },
users: { file: 'users.json', default: [] }
}
6 changes: 4 additions & 2 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,17 @@ services:
restart: always
tty: true
stop_signal: SIGINT
environment:
- SESSION_SECRET=mysupersecretkey
networks:
- zwave
devices:
- '/dev/ttyACM0:/dev/ttyACM0'
volumes:
- ./store:/usr/src/app/store
ports:
- '8091:8091'
- '3000:3000'
- '8091:8091' # port for web interface
- '3000:3000' # port for zwave-js websocket server
networks:
zwave:
# volumes:
Expand Down
2 changes: 2 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ services:
restart: always
tty: true
stop_signal: SIGINT
environment:
- SESSION_SECRET=mysupersecretkey
networks:
- zwave
devices:
Expand Down
2 changes: 2 additions & 0 deletions docs/guide/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This is the list of the actually supported env vars:

- `NETWORK_KEY`: Zwave Network key
- `HTTPS`: Enable https
- `SESSION_SECRET`: Used as secret for session. If not provided the default one is used
- `USE_SECURE_COOKIE`: Set the cookie [secure](https://github.com/expressjs/session#cookiesecure) option.
- `PORT`: The port to listen to for incoming requests. Default is `8091`
- `HOST`: The host address to bind to. Default is `0.0.0.0`
- `STORE_DIR`: The absolute path to the directory where all files will be stored. Default is `<path to your z2m dir>/store`
Expand Down
3 changes: 3 additions & 0 deletions docs/usage/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Firstly you need to open the browser at the link <http://localhost:8091> and edi

## General

- **Auth**: Enable this to password protect your application. Default credentials are:
- Username:`admin`
- Password: `zwave`
- **Log enabled**: Enable logging for zwavejs2mqtt
- **Log level**: Set the log level (Error, Warn, Info, Verbose, Debug, Silly)
- **Log to file**: Enable this to store the logs to a file
Expand Down
12 changes: 11 additions & 1 deletion lib/SocketManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,17 @@ SocketManager.prototype.bindServer = function (server) {

this.io = require('socket.io')(server)

this.io.on('connection', onConnection.bind(this))
this.io.use(this._authMiddleware()).on('connection', onConnection.bind(this))
}

SocketManager.prototype._authMiddleware = function () {
return (socket, next) => {
if (this.authMiddleware) {
this.authMiddleware(socket, next)
} else {
next()
}
}
}

/**
Expand Down
26 changes: 25 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const appRoot = require('app-root-path')
const path = require('path')
const { version } = require('../package.json')
const crypto = require('crypto')

let VERSION

Expand Down Expand Up @@ -175,6 +176,27 @@ function humanSize (bytes) {
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i]
}

async function hashPsw (password) {
return new Promise((resolve, reject) => {
const salt = crypto.randomBytes(8).toString('hex')

crypto.scrypt(password, salt, 64, (err, derivedKey) => {
if (err) reject(err)
resolve(salt + ':' + derivedKey.toString('hex'))
})
})
}

async function verifyPsw (password, hash) {
return new Promise((resolve, reject) => {
const [salt, key] = hash.split(':')
crypto.scrypt(password, salt, 64, (err, derivedKey) => {
if (err) reject(err)
resolve(key === derivedKey.toString('hex'))
})
})
}

module.exports = {
getPath,
joinPath,
Expand All @@ -185,5 +207,7 @@ module.exports = {
sanitizeTopic,
removeSlash,
hasProperty,
humanSize
humanSize,
hashPsw,
verifyPsw
}
175 changes: 175 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 7eef6c5

Please sign in to comment.