2022-12-12 23:02:32 +01:00
|
|
|
import mocks from "./mocks"
|
|
|
|
|
|
|
|
// init the licensing mock
|
|
|
|
import * as pro from "@budibase/pro"
|
|
|
|
mocks.licenses.init(pro)
|
|
|
|
|
|
|
|
// use unlimited license by default
|
|
|
|
mocks.licenses.useUnlimited()
|
|
|
|
|
2022-11-14 23:55:47 +01:00
|
|
|
import * as dbConfig from "../db"
|
2022-08-25 20:41:47 +02:00
|
|
|
dbConfig.init()
|
|
|
|
import env from "../environment"
|
2022-11-28 18:54:04 +01:00
|
|
|
import * as controllers from "./controllers"
|
2022-08-25 20:41:47 +02:00
|
|
|
const supertest = require("supertest")
|
2022-11-16 18:23:12 +01:00
|
|
|
import { Config } from "../constants"
|
2022-08-25 23:56:58 +02:00
|
|
|
import {
|
|
|
|
users,
|
|
|
|
tenancy,
|
|
|
|
sessions,
|
|
|
|
auth,
|
2022-11-11 12:10:07 +01:00
|
|
|
constants,
|
2022-11-11 16:43:41 +01:00
|
|
|
env as coreEnv,
|
2023-01-27 18:59:14 +01:00
|
|
|
utils,
|
|
|
|
DEFAULT_TENANT_ID,
|
2022-08-25 23:56:58 +02:00
|
|
|
} from "@budibase/backend-core"
|
2023-01-27 18:59:14 +01:00
|
|
|
import structures, { TENANT_ID, CSRF_TOKEN } from "./structures"
|
2022-08-25 20:41:47 +02:00
|
|
|
import { CreateUserResponse, User, AuthToken } from "@budibase/types"
|
2022-11-11 16:43:41 +01:00
|
|
|
import API from "./api"
|
2023-01-30 17:39:15 +01:00
|
|
|
import sdk from "../sdk"
|
2022-08-25 20:41:47 +02:00
|
|
|
|
|
|
|
enum Mode {
|
2022-11-11 12:10:07 +01:00
|
|
|
CLOUD = "cloud",
|
2022-08-25 20:41:47 +02:00
|
|
|
SELF = "self",
|
|
|
|
}
|
|
|
|
|
2023-01-30 19:51:22 +01:00
|
|
|
async function retry<T extends (...arg0: any[]) => any>(
|
2023-01-30 19:12:06 +01:00
|
|
|
fn: T,
|
|
|
|
maxTry: number = 5,
|
|
|
|
retryCount = 1
|
|
|
|
): Promise<Awaited<ReturnType<T>>> {
|
|
|
|
const currRetry = typeof retryCount === "number" ? retryCount : 1
|
|
|
|
try {
|
|
|
|
const result = await fn()
|
|
|
|
return result
|
|
|
|
} catch (e) {
|
|
|
|
console.log(`Retry ${currRetry} failed.`)
|
|
|
|
if (currRetry > maxTry) {
|
|
|
|
console.log(`All ${maxTry} retry attempts exhausted`)
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
return retry(fn, maxTry, currRetry + 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
class TestConfiguration {
|
|
|
|
server: any
|
|
|
|
request: any
|
2022-11-11 16:43:41 +01:00
|
|
|
api: API
|
2022-08-25 20:41:47 +02:00
|
|
|
defaultUser?: User
|
|
|
|
tenant1User?: User
|
2023-01-27 18:59:14 +01:00
|
|
|
#tenantId?: string
|
2022-08-25 20:41:47 +02:00
|
|
|
|
|
|
|
constructor(
|
|
|
|
opts: { openServer: boolean; mode: Mode } = {
|
|
|
|
openServer: true,
|
2022-11-11 12:10:07 +01:00
|
|
|
mode: Mode.CLOUD,
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
) {
|
2022-11-11 12:10:07 +01:00
|
|
|
if (opts.mode === Mode.CLOUD) {
|
|
|
|
this.modeCloud()
|
2022-08-25 20:41:47 +02:00
|
|
|
} else if (opts.mode === Mode.SELF) {
|
|
|
|
this.modeSelf()
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.openServer) {
|
|
|
|
env.PORT = "0" // random port
|
2023-01-11 10:37:37 +01:00
|
|
|
this.server = require("../index").default
|
2022-08-25 20:41:47 +02:00
|
|
|
// we need the request for logging in, involves cookies, hard to fake
|
|
|
|
this.request = supertest(this.server)
|
|
|
|
}
|
2022-11-11 16:43:41 +01:00
|
|
|
|
|
|
|
this.api = new API(this)
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getRequest() {
|
|
|
|
return this.request
|
|
|
|
}
|
|
|
|
|
|
|
|
// MODES
|
|
|
|
|
2022-11-11 12:10:07 +01:00
|
|
|
setMultiTenancy = (value: boolean) => {
|
|
|
|
env._set("MULTI_TENANCY", value)
|
|
|
|
coreEnv._set("MULTI_TENANCY", value)
|
|
|
|
}
|
|
|
|
|
|
|
|
setSelfHosted = (value: boolean) => {
|
|
|
|
env._set("SELF_HOSTED", value)
|
|
|
|
coreEnv._set("SELF_HOSTED", value)
|
|
|
|
}
|
|
|
|
|
|
|
|
modeCloud = () => {
|
|
|
|
this.setSelfHosted(false)
|
|
|
|
this.setMultiTenancy(true)
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
modeSelf = () => {
|
2022-11-11 12:10:07 +01:00
|
|
|
this.setSelfHosted(true)
|
|
|
|
this.setMultiTenancy(false)
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// UTILS
|
|
|
|
|
|
|
|
async _req(config: any, params: any, controlFunc: any) {
|
|
|
|
const request: any = {}
|
|
|
|
// fake cookies, we don't need them
|
|
|
|
request.cookies = { set: () => {}, get: () => {} }
|
|
|
|
request.config = { jwtSecret: env.JWT_SECRET }
|
|
|
|
request.user = { tenantId: this.getTenantId() }
|
|
|
|
request.query = {}
|
|
|
|
request.request = {
|
|
|
|
body: config,
|
|
|
|
}
|
|
|
|
request.throw = (status: any, err: any) => {
|
|
|
|
throw { status, message: err }
|
|
|
|
}
|
|
|
|
if (params) {
|
|
|
|
request.params = params
|
|
|
|
}
|
|
|
|
await tenancy.doInTenant(this.getTenantId(), () => {
|
|
|
|
return controlFunc(request)
|
|
|
|
})
|
|
|
|
return request.body
|
|
|
|
}
|
|
|
|
|
|
|
|
// SETUP / TEARDOWN
|
|
|
|
|
|
|
|
async beforeAll() {
|
2023-01-31 12:48:58 +01:00
|
|
|
try {
|
|
|
|
this.#tenantId = `tenant-${utils.newid()}`
|
|
|
|
|
|
|
|
// Running tests in parallel causes issues creating the globaldb twice. This ensures the db is properly created before starting
|
|
|
|
await retry(async () => await this.createDefaultUser())
|
|
|
|
await this.createSession(this.defaultUser!)
|
|
|
|
|
|
|
|
await tenancy.doInTenant(this.#tenantId, async () => {
|
|
|
|
await this.createTenant1User()
|
|
|
|
await this.createSession(this.tenant1User!)
|
|
|
|
})
|
|
|
|
} catch (e: any) {
|
2023-01-31 23:50:52 +01:00
|
|
|
console.log(e)
|
2023-01-31 12:48:58 +01:00
|
|
|
throw new Error(e.message)
|
|
|
|
}
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async afterAll() {
|
|
|
|
if (this.server) {
|
|
|
|
await this.server.close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TENANCY
|
|
|
|
|
2023-02-03 10:59:57 +01:00
|
|
|
createTenant = async (): Promise<User> => {
|
2022-11-11 12:10:07 +01:00
|
|
|
// create user / new tenant
|
2022-11-11 16:43:41 +01:00
|
|
|
const res = await this.api.users.createAdminUser()
|
|
|
|
|
2023-02-03 10:59:57 +01:00
|
|
|
await sdk.users.addTenant(res.tenantId, res.userId, res.email)
|
2023-01-30 17:39:15 +01:00
|
|
|
|
2022-11-11 16:43:41 +01:00
|
|
|
// return the created user
|
|
|
|
const userRes = await this.api.users.getUser(res.userId, {
|
|
|
|
headers: {
|
|
|
|
...this.internalAPIHeaders(),
|
2022-11-16 19:12:31 +01:00
|
|
|
[constants.Header.TENANT_ID]: res.tenantId,
|
2022-11-11 16:43:41 +01:00
|
|
|
},
|
2022-11-11 12:10:07 +01:00
|
|
|
})
|
2022-11-11 16:43:41 +01:00
|
|
|
|
|
|
|
// create a session for the new user
|
|
|
|
const user = userRes.body
|
|
|
|
await this.createSession(user)
|
|
|
|
|
|
|
|
return user
|
2022-11-11 12:10:07 +01:00
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
getTenantId() {
|
|
|
|
try {
|
|
|
|
return tenancy.getTenantId()
|
|
|
|
} catch (e: any) {
|
2023-01-27 18:59:14 +01:00
|
|
|
return DEFAULT_TENANT_ID
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-11 16:43:41 +01:00
|
|
|
// AUTH
|
2022-08-25 20:41:47 +02:00
|
|
|
|
2022-11-11 16:43:41 +01:00
|
|
|
async _createSession({
|
|
|
|
userId,
|
|
|
|
tenantId,
|
|
|
|
}: {
|
|
|
|
userId: string
|
|
|
|
tenantId: string
|
|
|
|
}) {
|
|
|
|
await sessions.createASession(userId!, {
|
|
|
|
sessionId: "sessionid",
|
|
|
|
tenantId: tenantId,
|
|
|
|
csrfToken: CSRF_TOKEN,
|
2022-08-25 20:41:47 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async createSession(user: User) {
|
2022-11-11 16:43:41 +01:00
|
|
|
return this._createSession({ userId: user._id!, tenantId: user.tenantId })
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cookieHeader(cookies: any) {
|
2022-11-16 12:34:16 +01:00
|
|
|
if (!Array.isArray(cookies)) {
|
|
|
|
cookies = [cookies]
|
|
|
|
}
|
2022-08-25 20:41:47 +02:00
|
|
|
return {
|
2022-11-16 12:34:16 +01:00
|
|
|
Cookie: cookies,
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
authHeaders(user: User) {
|
|
|
|
const authToken: AuthToken = {
|
|
|
|
userId: user._id!,
|
|
|
|
sessionId: "sessionid",
|
|
|
|
tenantId: user.tenantId,
|
|
|
|
}
|
2022-08-25 23:56:58 +02:00
|
|
|
const authCookie = auth.jwt.sign(authToken, env.JWT_SECRET)
|
2022-08-25 20:41:47 +02:00
|
|
|
return {
|
|
|
|
Accept: "application/json",
|
2022-11-16 19:12:31 +01:00
|
|
|
...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]),
|
|
|
|
[constants.Header.CSRF_TOKEN]: CSRF_TOKEN,
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultHeaders() {
|
|
|
|
const tenantId = this.getTenantId()
|
|
|
|
if (tenantId === TENANT_ID) {
|
|
|
|
return this.authHeaders(this.defaultUser!)
|
2023-01-27 18:59:14 +01:00
|
|
|
} else if (tenantId === this.getTenantId()) {
|
2022-08-25 20:41:47 +02:00
|
|
|
return this.authHeaders(this.tenant1User!)
|
|
|
|
} else {
|
|
|
|
throw new Error("could not determine auth headers to use")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-11 12:10:07 +01:00
|
|
|
internalAPIHeaders() {
|
2022-11-16 19:12:31 +01:00
|
|
|
return { [constants.Header.API_KEY]: env.INTERNAL_API_KEY }
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
|
2022-11-11 16:43:41 +01:00
|
|
|
adminOnlyResponse = () => {
|
|
|
|
return { message: "Admin user only endpoint.", status: 403 }
|
|
|
|
}
|
|
|
|
|
|
|
|
// USERS
|
|
|
|
|
|
|
|
async createDefaultUser() {
|
|
|
|
const user = structures.users.adminUser({
|
|
|
|
password: "test",
|
|
|
|
})
|
|
|
|
this.defaultUser = await this.createUser(user)
|
|
|
|
}
|
|
|
|
|
|
|
|
async createTenant1User() {
|
|
|
|
const user = structures.users.adminUser({
|
|
|
|
password: "test",
|
|
|
|
})
|
|
|
|
this.tenant1User = await this.createUser(user)
|
|
|
|
}
|
|
|
|
|
2022-08-25 20:41:47 +02:00
|
|
|
async getUser(email: string): Promise<User> {
|
|
|
|
return tenancy.doInTenant(this.getTenantId(), () => {
|
|
|
|
return users.getGlobalUserByEmail(email)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
async createUser(user?: User) {
|
|
|
|
if (!user) {
|
|
|
|
user = structures.users.user()
|
|
|
|
}
|
|
|
|
const response = await this._req(user, null, controllers.users.save)
|
|
|
|
const body = response as CreateUserResponse
|
|
|
|
return this.getUser(body.email)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CONFIGS
|
|
|
|
|
|
|
|
async deleteConfig(type: any) {
|
|
|
|
try {
|
|
|
|
const cfg = await this._req(
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
type,
|
|
|
|
},
|
|
|
|
controllers.config.find
|
|
|
|
)
|
|
|
|
if (cfg) {
|
|
|
|
await this._req(
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
id: cfg._id,
|
|
|
|
rev: cfg._rev,
|
|
|
|
},
|
|
|
|
controllers.config.destroy
|
|
|
|
)
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
// don't need to handle error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// CONFIGS - SETTINGS
|
|
|
|
|
|
|
|
async saveSettingsConfig() {
|
2022-11-16 18:23:12 +01:00
|
|
|
await this.deleteConfig(Config.SETTINGS)
|
2022-08-25 20:41:47 +02:00
|
|
|
await this._req(
|
|
|
|
structures.configs.settings(),
|
|
|
|
null,
|
|
|
|
controllers.config.save
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CONFIGS - GOOGLE
|
|
|
|
|
|
|
|
async saveGoogleConfig() {
|
2022-11-16 18:23:12 +01:00
|
|
|
await this.deleteConfig(Config.GOOGLE)
|
2022-08-25 20:41:47 +02:00
|
|
|
await this._req(structures.configs.google(), null, controllers.config.save)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CONFIGS - OIDC
|
|
|
|
|
|
|
|
getOIDConfigCookie(configId: string) {
|
2022-08-25 23:56:58 +02:00
|
|
|
const token = auth.jwt.sign(configId, env.JWT_SECRET)
|
2022-11-16 19:12:31 +01:00
|
|
|
return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]])
|
2022-08-25 20:41:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async saveOIDCConfig() {
|
2022-11-16 18:23:12 +01:00
|
|
|
await this.deleteConfig(Config.OIDC)
|
2022-08-25 20:41:47 +02:00
|
|
|
const config = structures.configs.oidc()
|
|
|
|
|
|
|
|
await this._req(config, null, controllers.config.save)
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
// CONFIGS - SMTP
|
|
|
|
|
|
|
|
async saveSmtpConfig() {
|
2022-11-16 18:23:12 +01:00
|
|
|
await this.deleteConfig(Config.SMTP)
|
2022-08-25 20:41:47 +02:00
|
|
|
await this._req(structures.configs.smtp(), null, controllers.config.save)
|
|
|
|
}
|
|
|
|
|
|
|
|
async saveEtherealSmtpConfig() {
|
2022-11-16 18:23:12 +01:00
|
|
|
await this.deleteConfig(Config.SMTP)
|
2022-08-25 20:41:47 +02:00
|
|
|
await this._req(
|
|
|
|
structures.configs.smtpEthereal(),
|
|
|
|
null,
|
|
|
|
controllers.config.save
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-11 10:37:37 +01:00
|
|
|
export default TestConfiguration
|