Merge branch 'develop' of github.com:Budibase/budibase into fix/sql-issues
This commit is contained in:
commit
89d04d5cc7
|
@ -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"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.9.173-alpha.1",
|
"version": "0.9.173-alpha.2",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
||||||
|
`;
|
|
@ -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()
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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({
|
||||||
|
|
Loading…
Reference in New Issue