update built-in objects in the database (#846)

Signed-off-by: Denis barbaron <denis.barbaron@orange.com>
This commit is contained in:
Denis Barbaron
2025-03-07 20:51:25 +01:00
committed by GitHub
parent 9de3828158
commit 4e8a5a1cef
3 changed files with 188 additions and 19 deletions

View File

@@ -1,5 +1,5 @@
/**
* Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
* Copyright © 2019-2025 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
**/
module.exports.command = 'migrate'
@@ -23,20 +23,23 @@ module.exports.handler = function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
return dbapi.getGroupByIndex(apiutil.ROOT, 'privilege').then(function(group) {
// signatures of built-in objects are defined
const env = {
STF_ROOT_GROUP_NAME: group ? group.name : 'Common'
, STF_ADMIN_NAME: group ? group.owner.name : 'administrator'
, STF_ADMIN_EMAIL: group ? group.owner.email : 'administrator@fakedomain.com'
}
for (const i in env) {
if (process.env[i]) {
env[i] = process.env[i]
}
}
if (!group) {
const env = {
STF_ROOT_GROUP_NAME: 'Common'
, STF_ADMIN_NAME: 'administrator'
, STF_ADMIN_EMAIL: 'administrator@fakedomain.com'
}
for (const i in env) {
if (process.env[i]) {
env[i] = process.env[i]
}
}
// root group does not exist, so bootstrap is created
return dbapi.createBootStrap(env)
}
return group
// bootstrap is updated with new signatures
return dbapi.updateBootStrap(group, env)
})
.then(function() {
resolve(true)

View File

@@ -14,6 +14,15 @@ const uuid = require('uuid')
const apiutil = require('../util/apiutil')
const Promise = require('bluebird')
const _ = require('lodash')
const logger = require('../util/logger')
const log = logger.createLogger('db:api')
function getDevices() {
return db.run(r.table('devices'))
.then(function(cursor) {
return cursor.toArray()
})
}
dbapi.DuplicateSecondaryIndexError = function DuplicateSecondaryIndexError() {
Error.call(this)
@@ -35,6 +44,156 @@ dbapi.unlockBookingObjects = function() {
])
}
dbapi.updateBootStrap = function(rootGroup, env) {
const rootGroupNameHasChanged = rootGroup.name !== env.STF_ROOT_GROUP_NAME
const adminEmailHasChanged = rootGroup.owner.email !== env.STF_ADMIN_EMAIL
const adminNameHasChanged = rootGroup.owner.name !== env.STF_ADMIN_NAME
function createNewAdminUser() {
if (!adminEmailHasChanged) {
if (adminNameHasChanged) {
log.error('Forbidden (user name cannot be changed)')
return Promise.resolve(false)
}
return Promise.resolve(true)
}
return dbapi.createUser(env.STF_ADMIN_EMAIL,
env.STF_ADMIN_NAME,
'127.0.0.1').then(function(stats) {
if (!stats.inserted) {
log.error('Forbidden (user already exists)')
return false
}
log.info('Created (user name:%s email:%s)'
, env.STF_ADMIN_NAME
, env.STF_ADMIN_EMAIL)
return dbapi.loadUser(rootGroup.owner.email).then(function(oldAdminUser) {
return db.run(r.table('users').get(env.STF_ADMIN_EMAIL).update({
privilege: oldAdminUser.privilege
, groups: oldAdminUser.groups
, settings: oldAdminUser.settings
}))
})
})
.catch(function(err) {
log.error('Failed to create user')
return Promise.reject(err)
})
}
function updateDevicesForMigration() {
return getDevices().then(function(devices) {
return Promise.map(devices, function(device) {
return db.run(r.table('devices').get(device.serial).update({
group: {
name:
r.branch(
r.expr(rootGroupNameHasChanged)
.eq(true)
.and(r.row('group')('id')
.eq(rootGroup.id))
, env.STF_ROOT_GROUP_NAME
, r.row('group')('name'))
, originName:
r.branch(
r.expr(rootGroupNameHasChanged)
.eq(true)
.and(r.row('group')('origin')
.eq(rootGroup.id))
, env.STF_ROOT_GROUP_NAME
, r.row('group')('originName'))
, owner:
r.branch(
r.expr(adminEmailHasChanged)
.eq(true)
.and(r.row('group')('id')
.eq(rootGroup.id))
, {
name: env.STF_ADMIN_NAME
, email: env.STF_ADMIN_EMAIL
}
, r.row('group')('owner'))
}
}))
})
})
}
function updateGroupsForMigration() {
if (rootGroupNameHasChanged && !adminEmailHasChanged) {
return db.run(r.table('groups').get(rootGroup.id).update({
name: env.STF_ROOT_GROUP_NAME
}))
}
return dbapi.getGroups().then(function(groups) {
return Promise.map(groups, function(group) {
return db.run(r.table('groups').get(group.id).update({
name:
r.branch(
r.expr(rootGroupNameHasChanged)
.eq(true)
.and(r.row('id')
.eq(rootGroup.id))
, env.STF_ROOT_GROUP_NAME
, r.row('name'))
, owner:
r.branch(
r.expr(adminEmailHasChanged)
.eq(true)
.and(r.row('owner')('email')
.eq(rootGroup.owner.email))
, {
name: env.STF_ADMIN_NAME
, email: env.STF_ADMIN_EMAIL
}
, r.row('owner'))
, users:
r.branch(
r.expr(adminEmailHasChanged)
.eq(true)
, _.union([env.STF_ADMIN_EMAIL], _.difference(group.users, [rootGroup.owner.email]))
, r.row('users'))
}))
})
})
}
return createNewAdminUser().then(function(success) {
if (!success || !rootGroupNameHasChanged && !adminEmailHasChanged) {
return false
}
return updateGroupsForMigration().then(function() {
return updateDevicesForMigration()
})
.then(function() {
if (adminEmailHasChanged) {
return dbapi.removeUserAccessTokens(rootGroup.owner.email)
}
return true
})
.then(function() {
if (adminEmailHasChanged) {
return dbapi.deleteUser(rootGroup.owner.email)
}
return true
})
})
.then(function(success) {
if (success) {
log.info('Built-in objects have been updated successfully')
}
return success
})
.catch(function(err) {
log.error('Failed to update built-in objects, potential data consistency issue')
return Promise.reject(err)
})
}
dbapi.createBootStrap = function(env) {
const now = Date.now()
@@ -78,13 +237,6 @@ dbapi.createBootStrap = function(env) {
})
}
function getDevices() {
return db.run(r.table('devices'))
.then(function(cursor) {
return cursor.toArray()
})
}
function updateDevicesForMigration(group) {
return getDevices().then(function(devices) {
return Promise.map(devices, function(device) {