budibase/packages/server/utilities/masterAppInternal.js

335 lines
9.3 KiB
JavaScript

const {
getApisWithFullAccess,
getApisForSession,
getMasterApisWithFullAccess,
} = require("./budibaseApi")
const getDatastore = require("./datastore")
const getDatabaseManager = require("./databaseManager")
const { $, splitKey } = require("@budibase/core").common
const { keyBy, values } = require("lodash/fp")
const {
masterAppPackage,
applictionVersionPackage,
applictionVersionPublicPaths,
} = require("../utilities/createAppPackage")
const { determineVersionId } = require("./runtimePackages")
const isMaster = appname => appname === "_master"
module.exports = async context => {
const { config } = context
const datastoreModule = getDatastore(config)
const databaseManager = getDatabaseManager(
datastoreModule,
config.datastoreConfig
)
const masterDatastore = datastoreModule.getDatastore(
databaseManager.masterDatastoreConfig
)
const bb = await getMasterApisWithFullAccess(context)
let applications
const loadApplications = async () => {
const apps = await bb.indexApi.listItems("/all_applications")
applications = $(apps, [keyBy("name")])
}
await loadApplications()
const getInstanceDatastore = instanceDatastoreConfig =>
datastoreModule.getDatastore(instanceDatastoreConfig)
const getCustomSessionId = (appname, sessionId) =>
isMaster(appname)
? bb.recordApi.customId("mastersession", sessionId)
: bb.recordApi.customId("session", sessionId)
const getApplication = async (nameOrKey, isRetry = false) => {
if (applications[nameOrKey]) return applications[nameOrKey]
for (let name in applications) {
const a = applications[name]
if (a.key === nameOrKey) return a
if (a.id === nameOrKey) return a
}
if (isRetry) return
await loadApplications()
return await getApplication(nameOrKey, true)
}
const getSession = async (sessionId, appname) => {
const customSessionId = getCustomSessionId(appname, sessionId)
if (isMaster(appname)) {
return await bb.recordApi.load(`/sessions/${customSessionId}`)
} else {
const app = await getApplication(appname)
return await bb.recordApi.load(
`/applications/${app.id}/sessions/${customSessionId}`
)
}
}
const deleteSession = async (sessionId, appname) => {
const customSessionId = getCustomSessionId(appname, sessionId)
if (isMaster(appname)) {
return await bb.recordApi.delete(`/sessions/${customSessionId}`)
} else {
const app = await getApplication(appname)
return await bb.recordApi.delete(
`/applications/${app.id}/sessions/${customSessionId}`
)
}
}
const createAppUser = async (appname, instance, user, password) => {
if (isMaster(appname)) {
throw new Error("This method is for creating app users - not on master!")
}
const versionId = determineVersionId(instance.version)
const dsConfig = JSON.parse(instance.datastoreconfig)
const appPackage = await applictionVersionPackage(
context,
appname,
versionId,
instance.key
)
const bbInstance = await getApisWithFullAccess(
datastoreModule.getDatastore(dsConfig),
appPackage
)
await bbInstance.authApi.createUser(user, password)
}
const authenticate = async (
sessionId,
appname,
username,
password,
instanceName = "default"
) => {
if (isMaster(appname)) {
const authUser = await bb.authApi.authenticate(username, password)
if (!authUser) {
return null
}
const session = bb.recordApi.getNew("/sessions", "mastersession")
bb.recordApi.setCustomId(session, sessionId)
session.user_json = JSON.stringify(authUser)
session.username = username
await bb.recordApi.save(session)
return session
}
const app = await getApplication(appname)
const userInMaster = await getUser(app.id, username)
if (!userInMaster) return null
const instance = await bb.recordApi.load(userInMaster.instance.key)
const versionId = determineVersionId(instance.version)
const dsConfig = JSON.parse(instance.datastoreconfig)
const appPackage = await applictionVersionPackage(
context,
appname,
versionId,
instance.key
)
const bbInstance = await getApisWithFullAccess(
datastoreModule.getDatastore(dsConfig),
appPackage
)
const authUser = await bbInstance.authApi.authenticate(username, password)
if (!authUser) {
return null
}
const session = bb.recordApi.getNew(
`/applications/${app.id}/sessions`,
"session"
)
bb.recordApi.setCustomId(session, sessionId)
session.user_json = JSON.stringify(authUser)
session.instanceDatastoreConfig = instance.datastoreconfig
session.instanceKey = instance.key
session.username = username
session.instanceVersion = instance.version.key
await bb.recordApi.save(session)
return session
}
const getInstanceApiForSession = async (appname, sessionId) => {
if (isMaster(appname)) {
const customId = bb.recordApi.customId("mastersession", sessionId)
const masterPkg = masterAppPackage(context)
try {
const session = await bb.recordApi.load(`/sessions/${customId}`)
return {
instance: await getApisForSession(
masterDatastore,
masterAppPackage(context),
session
),
publicPath: masterPkg.mainUiPath,
sharedPath: masterPkg.sharedPath,
}
} catch (_) {
return {
instance: null,
publicPath: masterPkg.unauthenticatedUiPath,
sharedPath: masterPkg.sharedPath,
}
}
} else {
const app = await getApplication(appname)
const customId = bb.recordApi.customId("session", sessionId)
try {
const session = await bb.recordApi.load(
`/applications/${app.id}/sessions/${customId}`
)
const dsConfig = JSON.parse(session.instanceDatastoreConfig)
const instanceDatastore = getInstanceDatastore(dsConfig)
const versionId = determineVersionId(session.instanceVersion)
const appPackage = await applictionVersionPackage(
context,
appname,
versionId,
session.instanceKey
)
return {
instance: await getApisForSession(
instanceDatastore,
appPackage,
session
),
publicPath: appPackage.mainUiPath,
sharedPath: appPackage.sharedPath,
}
} catch (_) {
const versionId = determineVersionId(app.defaultVersion)
const appPublicPaths = applictionVersionPublicPaths(
context,
app.name,
versionId
)
return {
instance: null,
publicPath: appPublicPaths.unauthenticatedUiPath,
sharedPath: appPublicPaths.sharedPath,
}
}
}
}
const getUser = async (appId, username) => {
const userId = bb.recordApi.customId("user", username)
try {
return await bb.recordApi.load(`/applications/${appId}/users/${userId}`)
} catch (_) {
//empty
return
}
}
const getFullAccessInstanceApiForUsername = async (appname, username) => {
if (isMaster(appname)) {
return bb
} else {
const app = await getApplication(appname)
const user = await getUser(app.id, username)
if (!user) return null
const dsConfig = JSON.parse(user.instance.datastoreconfig)
const instanceDatastore = getInstanceDatastore(dsConfig)
const versionId = determineVersionId(
(await bb.recordApi.load(user.instance.key)).version
)
const appPackage = await applictionVersionPackage(
context,
appname,
versionId,
user.instance.key
)
return await getApisWithFullAccess(instanceDatastore, appPackage)
}
}
const removeSessionsForUser = async (appname, username) => {
if (isMaster(appname)) {
const sessions = await bb.indexApi.listItems("/mastersessions_by_user", {
rangeStartParams: { username },
rangeEndParams: { username },
searchPhrase: `username:${username}`,
})
for (let session of sessions) {
await bb.recordApi.delete(session.key)
}
} else {
const app = await getApplication(appname)
const sessions = await bb.indexApi.listItems(
`/applications/${app.id}/sessions_by_user`,
{
rangeStartParams: { username },
rangeEndParams: { username },
searchPhrase: `username:${username}`,
}
)
for (let session of sessions) {
await bb.recordApi.delete(session.key)
}
}
}
const disableUser = async (app, username) => {
await removeSessionsForUser(app.name, username)
const userInMaster = await getUser(app.id, username)
userInMaster.active = false
await bb.recordApi.save(userInMaster)
}
const enableUser = async (app, username) => {
const userInMaster = await getUser(app.id, username)
userInMaster.active = true
await bb.recordApi.save(userInMaster)
}
const listApplications = () => values(applications)
return {
getApplication,
getSession,
deleteSession,
authenticate,
getInstanceApiForSession,
getFullAccessInstanceApiForUsername,
removeSessionsForUser,
disableUser,
enableUser,
getUser,
createAppUser,
bbMaster: bb,
listApplications,
}
}