@flowfuse/flowfuse
Version:
An open source low-code development platform
121 lines (107 loc) • 4.33 kB
JavaScript
const { compareHash, parseNrMqttId } = require('../utils')
module.exports = {
authenticateCredentials: async function (app, username, password) {
const parts = username.split('@')
if (parts.length === 2) {
const user = await app.db.models.TeamBrokerClient.byUsername(parts[0], parts[1])
if (!user) {
return false
}
if (user.Team.suspended) {
return false
}
await user.Team.ensureTeamTypeExists()
if (!user.Team.getFeatureProperty('teamBroker')) {
return false
}
if (!password || password.length > 128) {
return false
}
if (compareHash(password, user.password)) {
return true
}
}
return false
},
authenticateNrMqttNodeUser: async function (app, authId, clientId, password) {
// Basic checks
if (!password || password.length > 128) {
return false
}
const parsedUsername = parseNrMqttId(authId, 'username')
const parsedClientId = parseNrMqttId(clientId, 'clientId')
let clientIdValid = false
if (!parsedUsername.valid || !parsedClientId.valid) {
return false
}
// Check if the clientId matches the username
if (parsedUsername.teamId !== parsedClientId.teamId ||
parsedUsername.ownerType !== parsedClientId.ownerType ||
parsedUsername.ownerId !== parsedClientId.ownerId) {
return false // clientId and username must match (excluding HA ID)
}
if (parsedClientId.haId) {
clientIdValid = `${authId}:${parsedClientId.haId}` === clientId
} else {
clientIdValid = authId === clientId
}
if (!clientIdValid) {
return false // clientId must match the username
}
// get the user and validate its settings and password
const teamBrokerClient = await app.db.models.TeamBrokerClient.byUsername(parsedClientId.username, parsedClientId.teamId, true, true)
if (!teamBrokerClient) {
return false
}
if (teamBrokerClient.ownerType !== parsedUsername.ownerType) { // project or device
return false
}
if (teamBrokerClient.ownerType === 'device') {
if (teamBrokerClient.Device?.hashid !== parsedUsername.ownerId) {
return false
}
} else if (teamBrokerClient.ownerType === 'project') {
if (teamBrokerClient.Project?.id !== parsedUsername.ownerId) {
return false
}
}
if (teamBrokerClient.Team.suspended) {
return false
}
await teamBrokerClient.Team.ensureTeamTypeExists()
if (!teamBrokerClient.Team.getFeatureProperty('teamBroker')) {
return false
}
if (!compareHash(password, teamBrokerClient.password)) {
return false
}
return {
username: parsedClientId.username,
teamId: teamBrokerClient.Team.hashid,
clientIdValid
}
},
/**
* Update the MQTT password for the linked NR MQTT node team client user (if it exists)
* @param {*} app - the app instance
* @param {*} teamId - the team ID this client belongs to
* @param {'instance'|'device'} ownerType - the owner type (e.g. 'instance', 'device')
* @param {string} ownerId - the owner ID (e.g. project ID or device HASHID)
* @param {string} password - the new password
* @returns {boolean} - true if the password was updated, false if the user does not exist
* @throws {Error} - if user is found but it fails to update/save
*/
updateNtMqttNodeUserPassword: async function (app, teamId, ownerType, ownerId, password) {
if (ownerType === 'project') {
ownerType = 'instance'
}
const username = `${ownerType}:${ownerId}`
const existingClient = await app.db.models.TeamBrokerClient.byUsername(username, teamId)
if (existingClient) {
existingClient.password = password
await existingClient.save()
return true
}
return false
}
}