Merge branch 'develop' of github.com:Budibase/budibase into fix/sql-issues

This commit is contained in:
mike12345567 2021-10-27 14:51:43 +01:00
commit 89d04d5cc7
33 changed files with 307 additions and 95 deletions

28
.vscode/launch.json vendored
View File

@ -4,30 +4,18 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/app.js",
"skipFiles": [
"<node_internals>/**"
]
},
{
"type": "node",
"request": "launch",
"name": "Debug External",
"program": "${workspaceFolder}/packages/cli/bin/budi",
"args": [],
"cwd":"C:/code/my-apps",
"console": "externalTerminal"
},
{ {
"name": "Budibase Server", "name": "Budibase Server",
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"], "runtimeArgs": [
"args": ["${workspaceFolder}/packages/server/src/index.ts"], "--nolazy",
"-r",
"ts-node/register/transpile-only"
],
"args": [
"${workspaceFolder}/packages/server/src/index.ts"
],
"cwd": "${workspaceFolder}/packages/server" "cwd": "${workspaceFolder}/packages/server"
}, },
{ {

View File

@ -1,5 +1,5 @@
{ {
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/auth", "name": "@budibase/auth",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"description": "Authentication middlewares for budibase builder and apps", "description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -13,6 +13,7 @@ exports.DocumentTypes = {
APP_DEV: `${PRE_APP}${exports.SEPARATOR}${PRE_DEV}`, APP_DEV: `${PRE_APP}${exports.SEPARATOR}${PRE_DEV}`,
APP_METADATA: `${PRE_APP}${exports.SEPARATOR}metadata`, APP_METADATA: `${PRE_APP}${exports.SEPARATOR}metadata`,
ROLE: "role", ROLE: "role",
MIGRATIONS: "migrations",
} }
exports.StaticDatabases = { exports.StaticDatabases = {

View File

@ -21,7 +21,7 @@ exports.createUserEmailView = async db => {
// if using variables in a map function need to inject them before use // if using variables in a map function need to inject them before use
map: `function(doc) { map: `function(doc) {
if (doc._id.startsWith("${DocumentTypes.USER}")) { if (doc._id.startsWith("${DocumentTypes.USER}")) {
emit(doc.email, doc._id) emit(doc.email.toLowerCase(), doc._id)
} }
}`, }`,
} }

View File

@ -1,6 +1,6 @@
// Mock data // Mock data
require("./utilities/test-config") require("../../../tests/utilities/dbConfig")
const database = require("../../../db") const database = require("../../../db")
const { authenticateThirdParty } = require("../third-party-common") const { authenticateThirdParty } = require("../third-party-common")
@ -72,7 +72,6 @@ describe("third party common", () => {
const expectUserIsSynced = (user, thirdPartyUser) => { const expectUserIsSynced = (user, thirdPartyUser) => {
expect(user.provider).toBe(thirdPartyUser.provider) expect(user.provider).toBe(thirdPartyUser.provider)
expect(user.email).toBe(thirdPartyUser.email)
expect(user.firstName).toBe(thirdPartyUser.profile.name.givenName) expect(user.firstName).toBe(thirdPartyUser.profile.name.givenName)
expect(user.lastName).toBe(thirdPartyUser.profile.name.familyName) expect(user.lastName).toBe(thirdPartyUser.profile.name.familyName)
expect(user.thirdPartyProfile).toStrictEqual(thirdPartyUser.profile._json) expect(user.thirdPartyProfile).toStrictEqual(thirdPartyUser.profile._json)
@ -135,6 +134,24 @@ describe("third party common", () => {
}) })
}) })
describe("exists by email with different casing", () => {
beforeEach(async () => {
id = generateGlobalUserID(newid()) // random id
email = thirdPartyUser.email.toUpperCase() // matching email except for casing
await createUser()
})
it("syncs and authenticates the user", async () => {
await authenticateThirdParty(thirdPartyUser, true, done, saveUser)
const user = expectUserIsAuthenticated()
expectUserIsSynced(user, thirdPartyUser)
expectUserIsUpdated(user)
expect(user.email).toBe(thirdPartyUser.email.toUpperCase())
})
})
describe("exists by id", () => { describe("exists by id", () => {
beforeEach(async () => { beforeEach(async () => {
id = generateGlobalUserID(thirdPartyUser.userId) // matching id id = generateGlobalUserID(thirdPartyUser.userId) // matching id

View File

@ -66,12 +66,16 @@ exports.authenticateThirdParty = async function (
// setup a blank user using the third party id // setup a blank user using the third party id
dbUser = { dbUser = {
_id: userId, _id: userId,
email: thirdPartyUser.email,
roles: {}, roles: {},
} }
} }
dbUser = await syncUser(dbUser, thirdPartyUser) dbUser = await syncUser(dbUser, thirdPartyUser)
// never prompt for password reset
dbUser.forceResetPassword = false
// create or sync the user // create or sync the user
let response let response
try { try {
@ -122,9 +126,6 @@ async function syncUser(user, thirdPartyUser) {
user.provider = thirdPartyUser.provider user.provider = thirdPartyUser.provider
user.providerType = thirdPartyUser.providerType user.providerType = thirdPartyUser.providerType
// email
user.email = thirdPartyUser.email
if (thirdPartyUser.profile) { if (thirdPartyUser.profile) {
const profile = thirdPartyUser.profile const profile = thirdPartyUser.profile

View File

@ -0,0 +1,61 @@
const { DocumentTypes } = require("../db/constants")
const { getGlobalDB } = require("../tenancy")
exports.MIGRATION_DBS = {
GLOBAL_DB: "GLOBAL_DB",
}
exports.MIGRATIONS = {
USER_EMAIL_VIEW_CASING: "user_email_view_casing",
}
const DB_LOOKUP = {
[exports.MIGRATION_DBS.GLOBAL_DB]: [
exports.MIGRATIONS.USER_EMAIL_VIEW_CASING,
],
}
exports.getMigrationsDoc = async db => {
// get the migrations doc
try {
return await db.get(DocumentTypes.MIGRATIONS)
} catch (err) {
if (err.status && err.status === 404) {
return { _id: DocumentTypes.MIGRATIONS }
}
}
}
exports.migrateIfRequired = async (migrationDb, migrationName, migrateFn) => {
try {
let db
if (migrationDb === exports.MIGRATION_DBS.GLOBAL_DB) {
db = getGlobalDB()
} else {
throw new Error(`Unrecognised migration db [${migrationDb}]`)
}
if (!DB_LOOKUP[migrationDb].includes(migrationName)) {
throw new Error(
`Unrecognised migration name [${migrationName}] for db [${migrationDb}]`
)
}
const doc = await exports.getMigrationsDoc(db)
// exit if the migration has been performed
if (doc[migrationName]) {
return
}
console.log(`Performing migration: ${migrationName}`)
await migrateFn()
console.log(`Migration complete: ${migrationName}`)
// mark as complete
doc[migrationName] = Date.now()
await db.put(doc)
} catch (err) {
console.error(`Error performing migration: ${migrationName}: `, err)
throw err
}
}

View File

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`migrations should match snapshot 1`] = `
Object {
"_id": "migrations",
"_rev": "1-af6c272fe081efafecd2ea49a8fcbb40",
"user_email_view_casing": 1487076708000,
}
`;

View File

@ -0,0 +1,60 @@
require("../../tests/utilities/dbConfig")
const { migrateIfRequired, MIGRATION_DBS, MIGRATIONS, getMigrationsDoc } = require("../index")
const database = require("../../db")
const {
StaticDatabases,
} = require("../../db/utils")
Date.now = jest.fn(() => 1487076708000)
let db
describe("migrations", () => {
const migrationFunction = jest.fn()
beforeEach(() => {
db = database.getDB(StaticDatabases.GLOBAL.name)
})
afterEach(async () => {
jest.clearAllMocks()
await db.destroy()
})
const validMigration = () => {
return migrateIfRequired(MIGRATION_DBS.GLOBAL_DB, MIGRATIONS.USER_EMAIL_VIEW_CASING, migrationFunction)
}
it("should run a new migration", async () => {
await validMigration()
expect(migrationFunction).toHaveBeenCalled()
})
it("should match snapshot", async () => {
await validMigration()
const doc = await getMigrationsDoc(db)
expect(doc).toMatchSnapshot()
})
it("should skip a previously run migration", async () => {
await validMigration()
await validMigration()
expect(migrationFunction).toHaveBeenCalledTimes(1)
})
it("should reject an unknown migration name", async () => {
expect(async () => {
await migrateIfRequired(MIGRATION_DBS.GLOBAL_DB, "bogus_name", migrationFunction)
}).rejects.toThrow()
expect(migrationFunction).not.toHaveBeenCalled()
})
it("should reject an unknown database name", async () => {
expect(async () => {
await migrateIfRequired("bogus_db", MIGRATIONS.USER_EMAIL_VIEW_CASING, migrationFunction)
}).rejects.toThrow()
expect(migrationFunction).not.toHaveBeenCalled()
})
})

View File

@ -1,5 +1,5 @@
const PouchDB = require("pouchdb") const PouchDB = require("pouchdb")
const env = require("../../../../environment") const env = require("../../environment")
let POUCH_DB_DEFAULTS let POUCH_DB_DEFAULTS

View File

@ -1,3 +1,3 @@
const packageConfiguration = require("../../../../index") const packageConfiguration = require("../../index")
const CouchDB = require("./db") const CouchDB = require("./db")
packageConfiguration.init(CouchDB) packageConfiguration.init(CouchDB)

View File

@ -20,6 +20,9 @@ const { hash } = require("./hashing")
const userCache = require("./cache/user") const userCache = require("./cache/user")
const env = require("./environment") const env = require("./environment")
const { getUserSessions, invalidateSessions } = require("./security/sessions") const { getUserSessions, invalidateSessions } = require("./security/sessions")
const { migrateIfRequired } = require("./migrations")
const { USER_EMAIL_VIEW_CASING } = require("./migrations").MIGRATIONS
const { GLOBAL_DB } = require("./migrations").MIGRATION_DBS
const APP_PREFIX = DocumentTypes.APP + SEPARATOR const APP_PREFIX = DocumentTypes.APP + SEPARATOR
@ -128,10 +131,16 @@ exports.getGlobalUserByEmail = async email => {
throw "Must supply an email address to view" throw "Must supply an email address to view"
} }
const db = getGlobalDB() const db = getGlobalDB()
await migrateIfRequired(GLOBAL_DB, USER_EMAIL_VIEW_CASING, async () => {
// re-create the view with latest changes
await createUserEmailView(db)
})
try { try {
let users = ( let users = (
await db.query(`database/${ViewNames.USER_BY_EMAIL}`, { await db.query(`database/${ViewNames.USER_BY_EMAIL}`, {
key: email, key: email.toLowerCase(),
include_docs: true, include_docs: true,
}) })
).rows ).rows

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.173-alpha.1", "@budibase/bbui": "^0.9.173-alpha.2",
"@budibase/client": "^0.9.173-alpha.1", "@budibase/client": "^0.9.173-alpha.2",
"@budibase/colorpicker": "1.1.2", "@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.173-alpha.1", "@budibase/string-templates": "^0.9.173-alpha.2",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.173-alpha.1", "@budibase/bbui": "^0.9.173-alpha.2",
"@budibase/standard-components": "^0.9.139", "@budibase/standard-components": "^0.9.139",
"@budibase/string-templates": "^0.9.173-alpha.1", "@budibase/string-templates": "^0.9.173-alpha.2",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -68,9 +68,9 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.173-alpha.1", "@budibase/auth": "^0.9.173-alpha.2",
"@budibase/client": "^0.9.173-alpha.1", "@budibase/client": "^0.9.173-alpha.2",
"@budibase/string-templates": "^0.9.173-alpha.1", "@budibase/string-templates": "^0.9.173-alpha.2",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0", "@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1", "@sendgrid/mail": "7.1.1",

View File

@ -8,3 +8,5 @@ env._set("CLIENT_ID", "test-client-id")
env._set("BUDIBASE_DIR", tmpdir("budibase-unittests")) env._set("BUDIBASE_DIR", tmpdir("budibase-unittests"))
env._set("LOG_LEVEL", "silent") env._set("LOG_LEVEL", "silent")
env._set("PORT", 0) env._set("PORT", 0)
global.console.log = jest.fn() // console.log are ignored in tests

View File

@ -13,8 +13,7 @@ describe("/authenticate", () => {
describe("fetch self", () => { describe("fetch self", () => {
it("should be able to fetch self", async () => { it("should be able to fetch self", async () => {
await config.createUser("test@test.com", "p4ssw0rd") const headers = await config.login()
const headers = await config.login("test@test.com", "p4ssw0rd", { userId: "us_uuid1" })
const res = await request const res = await request
.get(`/api/self`) .get(`/api/self`)
.set(headers) .set(headers)

View File

@ -89,6 +89,9 @@ describe("/permission", () => {
describe("check public user allowed", () => { describe("check public user allowed", () => {
it("should be able to read the row", async () => { it("should be able to read the row", async () => {
// replicate changes before checking permissions
await config.deploy()
const res = await request const res = await request
.get(`/api/${table._id}/rows`) .get(`/api/${table._id}/rows`)
.set(config.publicHeaders()) .set(config.publicHeaders())

View File

@ -93,9 +93,9 @@ describe("/queries", () => {
const query = await config.createQuery() const query = await config.createQuery()
const res = await request const res = await request
.get(`/api/queries/${query._id}`) .get(`/api/queries/${query._id}`)
.set(await config.roleHeaders({})) .set(await config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200) .expect(200)
.expect("Content-Type", /json/)
expect(res.body.fields).toBeUndefined() expect(res.body.fields).toBeUndefined()
expect(res.body.parameters).toBeUndefined() expect(res.body.parameters).toBeUndefined()
expect(res.body.schema).toBeUndefined() expect(res.body.schema).toBeUndefined()

View File

@ -21,16 +21,31 @@ describe("/routing", () => {
screen2.routing.roleId = BUILTIN_ROLE_IDS.POWER screen2.routing.roleId = BUILTIN_ROLE_IDS.POWER
screen2.routing.route = route screen2.routing.route = route
screen2 = await config.createScreen(screen2) screen2 = await config.createScreen(screen2)
await config.deploy()
}) })
describe("fetch", () => { describe("fetch", () => {
it("prevents a public user from accessing development app", async () => {
await request
.get(`/api/routing/client`)
.set(config.publicHeaders({ prodApp: false }))
.expect(302)
})
it("prevents a non builder from accessing development app", async () => {
await request
.get(`/api/routing/client`)
.set(await config.roleHeaders({
roleId: BUILTIN_ROLE_IDS.BASIC,
prodApp: false
}))
.expect(302)
})
it("returns the correct routing for basic user", async () => { it("returns the correct routing for basic user", async () => {
const res = await request const res = await request
.get(`/api/routing/client`) .get(`/api/routing/client`)
.set(await config.roleHeaders({ .set(await config.roleHeaders({
email: "basic@test.com", roleId: BUILTIN_ROLE_IDS.BASIC
roleId: BUILTIN_ROLE_IDS.BASIC,
builder: false
})) }))
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -49,9 +64,7 @@ describe("/routing", () => {
const res = await request const res = await request
.get(`/api/routing/client`) .get(`/api/routing/client`)
.set(await config.roleHeaders({ .set(await config.roleHeaders({
email: "basic@test.com", roleId: BUILTIN_ROLE_IDS.POWER
roleId: BUILTIN_ROLE_IDS.POWER,
builder: false,
})) }))
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
@ -71,9 +84,12 @@ describe("/routing", () => {
it("should fetch all routes for builder", async () => { it("should fetch all routes for builder", async () => {
const res = await request const res = await request
.get(`/api/routing`) .get(`/api/routing`)
.set(config.defaultHeaders()) .set(await config.roleHeaders({
.expect("Content-Type", /json/) roleId: BUILTIN_ROLE_IDS.POWER,
builder: true,
}))
.expect(200) .expect(200)
.expect("Content-Type", /json/)
expect(res.body.routes).toBeDefined() expect(res.body.routes).toBeDefined()
expect(res.body.routes[route].subpaths[route]).toBeDefined() expect(res.body.routes[route].subpaths[route]).toBeDefined()
const subpath = res.body.routes[route].subpaths[route] const subpath = res.body.routes[route].subpaths[route]

View File

@ -38,7 +38,7 @@ describe("/users", () => {
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
await config.createUser("brenda@brenda.com", "brendas_password") await config.createUser()
await checkPermissionsEndpoint({ await checkPermissionsEndpoint({
config, config,
request, request,

View File

@ -50,9 +50,10 @@ exports.createRequest = (request, method, url, body) => {
} }
exports.checkBuilderEndpoint = async ({ config, method, url, body }) => { exports.checkBuilderEndpoint = async ({ config, method, url, body }) => {
const headers = await config.login("test@test.com", "test", { const headers = await config.login({
userId: "us_fail", userId: "us_fail",
builder: false, builder: false,
prodApp: true,
}) })
await exports await exports
.createRequest(config.request, method, url, body) .createRequest(config.request, method, url, body)
@ -68,11 +69,9 @@ exports.checkPermissionsEndpoint = async ({
passRole, passRole,
failRole, failRole,
}) => { }) => {
const password = "PASSWORD" const passHeader = await config.login({
let user = await config.createUser("pass@budibase.com", password, passRole)
const passHeader = await config.login("pass@budibase.com", password, {
roleId: passRole, roleId: passRole,
userId: user.globalId, prodApp: true,
}) })
await exports await exports
@ -82,13 +81,12 @@ exports.checkPermissionsEndpoint = async ({
let failHeader let failHeader
if (failRole === BUILTIN_ROLE_IDS.PUBLIC) { if (failRole === BUILTIN_ROLE_IDS.PUBLIC) {
failHeader = config.publicHeaders() failHeader = config.publicHeaders({ prodApp: true })
} else { } else {
user = await config.createUser("fail@budibase.com", password, failRole) failHeader = await config.login({
failHeader = await config.login("fail@budibase.com", password, {
roleId: failRole, roleId: failRole,
userId: user.globalId,
builder: false, builder: false,
prodApp: true,
}) })
} }

View File

@ -112,8 +112,11 @@ describe("/webhooks", () => {
describe("trigger", () => { describe("trigger", () => {
it("should allow triggering from public", async () => { it("should allow triggering from public", async () => {
// replicate changes before checking webhook
await config.deploy()
const res = await request const res = await request
.post(`/api/webhooks/trigger/${config.getAppId()}/${webhook._id}`) .post(`/api/webhooks/trigger/${config.prodAppId}/${webhook._id}`)
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.message).toBeDefined() expect(res.body.message).toBeDefined()

View File

@ -3,7 +3,7 @@ const { getAppId, setCookie, getCookie, clearCookie } =
const { Cookies } = require("@budibase/auth").constants const { Cookies } = require("@budibase/auth").constants
const { getRole } = require("@budibase/auth/roles") const { getRole } = require("@budibase/auth/roles")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { generateUserMetadataID } = require("../db/utils") const { generateUserMetadataID, isDevAppID } = require("../db/utils")
const { dbExists } = require("@budibase/auth/db") const { dbExists } = require("@budibase/auth/db")
const { isUserInAppTenant } = require("@budibase/auth/tenancy") const { isUserInAppTenant } = require("@budibase/auth/tenancy")
const { getCachedSelf } = require("../utilities/global") const { getCachedSelf } = require("../utilities/global")
@ -35,6 +35,15 @@ module.exports = async (ctx, next) => {
requestAppId = requestAppId || appId requestAppId = requestAppId || appId
} }
// deny access to application preview
if (
isDevAppID(requestAppId) &&
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global)
) {
clearCookie(ctx, Cookies.CurrentApp)
return ctx.redirect("/")
}
let appId, let appId,
roleId = BUILTIN_ROLE_IDS.PUBLIC roleId = BUILTIN_ROLE_IDS.PUBLIC
if (!ctx.user) { if (!ctx.user) {

View File

@ -26,7 +26,6 @@ auth.init(CouchDB)
const GLOBAL_USER_ID = "us_uuid1" const GLOBAL_USER_ID = "us_uuid1"
const EMAIL = "babs@babs.com" const EMAIL = "babs@babs.com"
const PASSWORD = "babs_password"
class TestConfiguration { class TestConfiguration {
constructor(openServer = true) { constructor(openServer = true) {
@ -67,13 +66,18 @@ class TestConfiguration {
return request.body return request.body
} }
async globalUser(id = GLOBAL_USER_ID, builder = true, roles) { async globalUser({
id = GLOBAL_USER_ID,
builder = true,
email = EMAIL,
roles,
} = {}) {
const db = getGlobalDB(TENANT_ID) const db = getGlobalDB(TENANT_ID)
let existing let existing
try { try {
existing = await db.get(id) existing = await db.get(id)
} catch (err) { } catch (err) {
existing = {} existing = { email }
} }
const user = { const user = {
_id: id, _id: id,
@ -84,6 +88,8 @@ class TestConfiguration {
await createASession(id, { sessionId: "sessionid", tenantId: TENANT_ID }) await createASession(id, { sessionId: "sessionid", tenantId: TENANT_ID })
if (builder) { if (builder) {
user.builder = { global: true } user.builder = { global: true }
} else {
user.builder = { global: false }
} }
const resp = await db.put(user) const resp = await db.put(user)
return { return {
@ -132,12 +138,14 @@ class TestConfiguration {
return headers return headers
} }
publicHeaders() { publicHeaders({ prodApp = true } = {}) {
const appId = prodApp ? this.prodAppId : this.appId
const headers = { const headers = {
Accept: "application/json", Accept: "application/json",
} }
if (this.appId) { if (appId) {
headers[Headers.APP_ID] = this.appId headers[Headers.APP_ID] = appId
} }
return headers return headers
} }
@ -146,17 +154,37 @@ class TestConfiguration {
email = EMAIL, email = EMAIL,
roleId = BUILTIN_ROLE_IDS.ADMIN, roleId = BUILTIN_ROLE_IDS.ADMIN,
builder = false, builder = false,
}) { prodApp = true,
return this.login(email, PASSWORD, { roleId, builder }) } = {}) {
return this.login({ email, roleId, builder, prodApp })
} }
async createApp(appName) { async createApp(appName) {
// create dev app
this.app = await this._req({ name: appName }, null, controllers.app.create) this.app = await this._req({ name: appName }, null, controllers.app.create)
this.appId = this.app.appId this.appId = this.app.appId
// create production app
this.prodApp = await this.deploy()
this.prodAppId = this.prodApp.appId
this.allApps.push(this.prodApp)
this.allApps.push(this.app) this.allApps.push(this.app)
return this.app return this.app
} }
async deploy() {
const deployment = await this._req(null, null, controllers.deploy.deployApp)
const prodAppId = deployment.appId.replace("_dev", "")
const appPackage = await this._req(
null,
{ appId: prodAppId },
controllers.app.fetchAppPackage
)
return appPackage.application
}
async updateTable(config = null) { async updateTable(config = null) {
config = config || basicTable() config = config || basicTable()
this.table = await this._req(config, null, controllers.table.save) this.table = await this._req(config, null, controllers.table.save)
@ -314,9 +342,9 @@ class TestConfiguration {
return await this._req(config, null, controllers.layout.save) return await this._req(config, null, controllers.layout.save)
} }
async createUser(id = null) { async createUser(id = null, email = EMAIL) {
const globalId = !id ? `us_${Math.random()}` : `us_${id}` const globalId = !id ? `us_${Math.random()}` : `us_${id}`
const resp = await this.globalUser(globalId) const resp = await this.globalUser({ id: globalId, email })
await userCache.invalidateUser(globalId) await userCache.invalidateUser(globalId)
return { return {
...resp, ...resp,
@ -324,21 +352,21 @@ class TestConfiguration {
} }
} }
async login(email, password, { roleId, userId, builder } = {}) { async login({ roleId, userId, builder, prodApp = false } = {}) {
const appId = prodApp ? this.prodAppId : this.appId
userId = !userId ? `us_uuid1` : userId userId = !userId ? `us_uuid1` : userId
if (!this.request) { if (!this.request) {
throw "Server has not been opened, cannot login." throw "Server has not been opened, cannot login."
} }
// make sure the user exists in the global DB // make sure the user exists in the global DB
if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) { if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) {
const appId = `app${this.getAppId().split("app_dev")[1]}` await this.globalUser({
await this.globalUser(userId, builder, { userId,
[appId]: roleId, builder,
roles: { [this.prodAppId]: roleId },
}) })
} }
if (!email || !password) {
await this.createUser()
}
await createASession(userId, { await createASession(userId, {
sessionId: "sessionid", sessionId: "sessionid",
tenantId: TENANT_ID, tenantId: TENANT_ID,
@ -351,7 +379,7 @@ class TestConfiguration {
} }
const app = { const app = {
roleId: roleId, roleId: roleId,
appId: this.appId, appId,
} }
const authToken = jwt.sign(auth, env.JWT_SECRET) const authToken = jwt.sign(auth, env.JWT_SECRET)
const appToken = jwt.sign(app, env.JWT_SECRET) const appToken = jwt.sign(app, env.JWT_SECRET)
@ -364,7 +392,7 @@ class TestConfiguration {
`${Cookies.Auth}=${authToken}`, `${Cookies.Auth}=${authToken}`,
`${Cookies.CurrentApp}=${appToken}`, `${Cookies.CurrentApp}=${appToken}`,
], ],
[Headers.APP_ID]: this.appId, [Headers.APP_ID]: appId,
} }
} }
} }

View File

@ -12,4 +12,5 @@ module.exports = {
screen: require("../../api/controllers/screen"), screen: require("../../api/controllers/screen"),
webhook: require("../../api/controllers/webhook"), webhook: require("../../api/controllers/webhook"),
layout: require("../../api/controllers/layout"), layout: require("../../api/controllers/layout"),
deploy: require("../../api/controllers/deploy"),
} }

View File

@ -82,6 +82,13 @@ class InMemoryQueue {
// TODO: implement for testing // TODO: implement for testing
console.log(cronJobId) console.log(cronJobId)
} }
/**
* Implemented for tests
*/
getRepeatableJobs() {
return []
}
} }
module.exports = InMemoryQueue module.exports = InMemoryQueue

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.173-alpha.1", "version": "0.9.173-alpha.2",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -29,8 +29,8 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.173-alpha.1", "@budibase/auth": "^0.9.173-alpha.2",
"@budibase/string-templates": "^0.9.173-alpha.1", "@budibase/string-templates": "^0.9.173-alpha.2",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"@sentry/node": "^6.0.0", "@sentry/node": "^6.0.0",
"@techpass/passport-openidconnect": "^0.3.0", "@techpass/passport-openidconnect": "^0.3.0",

View File

@ -24,7 +24,7 @@ describe("/api/global/auth", () => {
// initially configure settings // initially configure settings
await config.saveSmtpConfig() await config.saveSmtpConfig()
await config.saveSettingsConfig() await config.saveSettingsConfig()
await config.createUser("test@test.com") await config.createUser()
const res = await request const res = await request
.post(`/api/global/auth/${TENANT_ID}/reset`) .post(`/api/global/auth/${TENANT_ID}/reset`)
.send({ .send({