Adding the sync call from the worker for creation, updating and deletion of users. Making sure that production and development apps are always up to date with user metadata.
This commit is contained in:
parent
3a5e004f36
commit
94ee13ffc4
|
@ -45,6 +45,7 @@ services:
|
|||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
|
||||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
|
||||
MINIO_URL: http://minio-service:9000
|
||||
APPS_URL: http://app-service:4002
|
||||
COUCH_DB_USERNAME: ${COUCH_DB_USER}
|
||||
COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD}
|
||||
COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984
|
||||
|
|
|
@ -113,6 +113,8 @@ spec:
|
|||
value: {{ .Values.globals.smtp.port | quote }}
|
||||
- name: SMTP_FROM_ADDRESS
|
||||
value: {{ .Values.globals.smtp.from | quote }}
|
||||
- name: APPS_URL
|
||||
value: http://app-service:{{ .Values.services.apps.port }}
|
||||
image: budibase/worker
|
||||
imagePullPolicy: Always
|
||||
name: bbworker
|
||||
|
|
|
@ -8,8 +8,13 @@ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global")
|
|||
const { getFullUser } = require("../../utilities/users")
|
||||
const { isEqual } = require("lodash")
|
||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||
const { getDevelopmentAppID } = require("@budibase/auth/db")
|
||||
const {
|
||||
getDevelopmentAppID,
|
||||
getAllApps,
|
||||
isDevAppID,
|
||||
} = require("@budibase/auth/db")
|
||||
const { doesDatabaseExist } = require("../../utilities")
|
||||
const { UserStatus } = require("@budibase/auth/constants")
|
||||
|
||||
async function rawMetadata(db) {
|
||||
return (
|
||||
|
@ -21,7 +26,7 @@ async function rawMetadata(db) {
|
|||
).rows.map(row => row.doc)
|
||||
}
|
||||
|
||||
async function combineMetadataAndUser(user, metadata) {
|
||||
function combineMetadataAndUser(user, metadata) {
|
||||
// skip users with no access
|
||||
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
|
||||
return null
|
||||
|
@ -67,22 +72,61 @@ exports.syncGlobalUsers = async appId => {
|
|||
}
|
||||
|
||||
exports.syncUser = async function (ctx) {
|
||||
const user = await getRawGlobalUser(ctx.params.id)
|
||||
const roles = user.roles
|
||||
delete user.roles
|
||||
for (let [prodAppId, roleId] of Object.entries(roles)) {
|
||||
if (roleId === BUILTIN_ROLE_IDS.PUBLIC) {
|
||||
continue
|
||||
let deleting = false,
|
||||
user
|
||||
const userId = ctx.params.id
|
||||
try {
|
||||
user = await getRawGlobalUser(userId)
|
||||
} catch (err) {
|
||||
user = {}
|
||||
deleting = true
|
||||
}
|
||||
const roles = user.roles
|
||||
// remove props which aren't useful to metadata
|
||||
delete user.password
|
||||
delete user.forceResetPassword
|
||||
delete user.roles
|
||||
// run through all production appIDs in the users roles
|
||||
let prodAppIds
|
||||
// if they are a builder then get all production app IDs
|
||||
if ((user.builder && user.builder.global) || deleting) {
|
||||
prodAppIds = (await getAllApps(CouchDB, { idsOnly: true })).filter(
|
||||
id => !isDevAppID(id)
|
||||
)
|
||||
} else {
|
||||
prodAppIds = Object.entries(roles)
|
||||
.filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
|
||||
.map(([appId]) => appId)
|
||||
}
|
||||
for (let prodAppId of prodAppIds) {
|
||||
const devAppId = getDevelopmentAppID(prodAppId)
|
||||
for (let appId of [prodAppId, devAppId]) {
|
||||
if (!(await doesDatabaseExist(appId))) {
|
||||
continue
|
||||
}
|
||||
const db = new CouchDB(appId)
|
||||
const userId = generateUserMetadataID(user._id)
|
||||
const metadata = await db.get(userId)
|
||||
const combined = combineMetadataAndUser(user, metadata)
|
||||
const metadataId = generateUserMetadataID(userId)
|
||||
let metadata
|
||||
try {
|
||||
metadata = await db.get(metadataId)
|
||||
} catch (err) {
|
||||
if (deleting) {
|
||||
continue
|
||||
}
|
||||
metadata = {
|
||||
tableId: InternalTables.USER_METADATA,
|
||||
}
|
||||
}
|
||||
let combined
|
||||
if (deleting) {
|
||||
combined = {
|
||||
...metadata,
|
||||
status: UserStatus.INACTIVE,
|
||||
metadata: BUILTIN_ROLE_IDS.PUBLIC,
|
||||
}
|
||||
} else {
|
||||
combined = combineMetadataAndUser(user, metadata)
|
||||
}
|
||||
await db.put(combined)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ module.exports =
|
|||
(permType, permLevel = null) =>
|
||||
async (ctx, next) => {
|
||||
// webhooks don't need authentication, each webhook unique
|
||||
if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) {
|
||||
// also internal requests (between services) don't need authorized
|
||||
if (WEBHOOK_ENDPOINTS.test(ctx.request.url) || ctx.internal) {
|
||||
return next()
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ exports.getGlobalUsers = async (appId = null, users = null) => {
|
|||
.filter(user => user != null)
|
||||
.map(user => {
|
||||
delete user.password
|
||||
delete user.forceResetPassword
|
||||
return user
|
||||
})
|
||||
if (!appId) {
|
||||
|
|
|
@ -136,7 +136,11 @@ exports.stringToReadStream = string => {
|
|||
}
|
||||
|
||||
exports.doesDatabaseExist = async dbName => {
|
||||
try {
|
||||
const db = new CouchDB(dbName, { skip_setup: true })
|
||||
const info = await db.info()
|
||||
return info && !info.error
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ async function init() {
|
|||
ACCOUNT_PORTAL_URL: "http://localhost:10001",
|
||||
ACCOUNT_PORTAL_API_KEY: "budibase",
|
||||
PLATFORM_URL: "http://localhost:10000",
|
||||
APPS_URL: "http://localhost:4001",
|
||||
}
|
||||
let envFile = ""
|
||||
Object.keys(envFileJson).forEach(key => {
|
||||
|
|
|
@ -19,6 +19,7 @@ const {
|
|||
} = require("@budibase/auth/tenancy")
|
||||
const { removeUserFromInfoDB } = require("@budibase/auth/deprovision")
|
||||
const env = require("../../../environment")
|
||||
const { syncUserInApps } = require("../../../utilities/appService")
|
||||
|
||||
async function allUsers() {
|
||||
const db = getGlobalDB()
|
||||
|
@ -32,7 +33,10 @@ async function allUsers() {
|
|||
|
||||
exports.save = async ctx => {
|
||||
try {
|
||||
ctx.body = await saveUser(ctx.request.body, getTenantId())
|
||||
const user = await saveUser(ctx.request.body, getTenantId())
|
||||
// let server know to sync user
|
||||
await syncUserInApps(user._id)
|
||||
ctx.body = user
|
||||
} catch (err) {
|
||||
ctx.throw(err.status || 400, err)
|
||||
}
|
||||
|
@ -129,6 +133,8 @@ exports.destroy = async ctx => {
|
|||
await db.remove(dbUser._id, dbUser._rev)
|
||||
await userCache.invalidateUser(dbUser._id)
|
||||
await invalidateSessions(dbUser._id)
|
||||
// let server know to sync user
|
||||
await syncUserInApps(dbUser._id)
|
||||
ctx.body = {
|
||||
message: `User ${ctx.params.id} deleted.`,
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ module.exports = {
|
|||
SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS,
|
||||
PLATFORM_URL: process.env.PLATFORM_URL,
|
||||
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
|
||||
APPS_URL: process.env.APPS_URL,
|
||||
_set(key, value) {
|
||||
process.env[key] = value
|
||||
module.exports[key] = value
|
||||
|
@ -53,6 +54,13 @@ module.exports = {
|
|||
},
|
||||
}
|
||||
|
||||
// if some var haven't been set, define them
|
||||
if (!module.exports.APPS_URL) {
|
||||
module.exports.APPS_URL = isDev()
|
||||
? "http://localhost:4001"
|
||||
: "http://app-service:4002"
|
||||
}
|
||||
|
||||
// clean up any environment variable edge cases
|
||||
for (let [key, value] of Object.entries(module.exports)) {
|
||||
// handle the edge case of "0" to disable an environment variable
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
const fetch = require("node-fetch")
|
||||
const { Headers } = require("@budibase/auth/constants")
|
||||
const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy")
|
||||
const { checkSlashesInUrl } = require("../utilities")
|
||||
const env = require("../environment")
|
||||
|
||||
exports.syncUserInApps = async userId => {
|
||||
const request = { headers: {} }
|
||||
request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY
|
||||
if (isTenantIdSet()) {
|
||||
request.headers[Headers.TENANT_ID] = getTenantId()
|
||||
}
|
||||
request.headers["Content-Type"] = "application/json"
|
||||
request.body = JSON.stringify({})
|
||||
request.method = "POST"
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.APPS_URL + `/api/users/sync/${userId}`),
|
||||
request
|
||||
)
|
||||
if (response.status !== 200) {
|
||||
throw "Unable to sync user."
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue