From b86a6fddc9d298304a218a8bd19ae07ff36d1a40 Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Wed, 4 Aug 2021 10:02:24 +0100 Subject: [PATCH] Revert "Multi-tenancy/organisations" --- hosting/envoy.dev.yaml.hbs | 8 - hosting/envoy.yaml | 10 +- package.json | 4 +- packages/auth/db.js | 5 +- packages/auth/package.json | 1 - packages/auth/src/cache/user.js | 14 +- packages/auth/src/constants.js | 5 +- packages/auth/src/db/constants.js | 17 - packages/auth/src/db/utils.js | 95 ++- packages/auth/src/db/views.js | 6 +- packages/auth/src/environment.js | 1 - packages/auth/src/index.js | 7 +- packages/auth/src/middleware/authenticated.js | 58 +- packages/auth/src/middleware/index.js | 2 - packages/auth/src/middleware/matchers.js | 33 - .../auth/src/middleware/passport/google.js | 10 +- .../auth/src/middleware/passport/local.js | 18 +- .../middleware/passport/tests/google.spec.js | 10 +- .../middleware/passport/third-party-common.js | 23 +- packages/auth/src/middleware/tenancy.js | 14 - packages/auth/src/security/sessions.js | 7 +- packages/auth/src/tenancy/FunctionContext.js | 73 --- packages/auth/src/tenancy/context.js | 81 --- packages/auth/src/tenancy/index.js | 4 - packages/auth/src/tenancy/tenancy.js | 105 --- packages/auth/src/utils.js | 13 +- packages/auth/tenancy.js | 1 - packages/auth/yarn.lock | 35 +- .../builder/src/pages/builder/_layout.svelte | 14 +- .../src/pages/builder/admin/index.svelte | 28 +- .../auth/_components/GoogleButton.svelte | 8 +- .../auth/_components/OIDCButton.svelte | 2 +- .../src/pages/builder/auth/forgot.svelte | 11 +- .../src/pages/builder/auth/index.svelte | 22 +- .../src/pages/builder/auth/login.svelte | 23 +- .../builder/src/pages/builder/auth/org.svelte | 73 --- .../builder/src/pages/builder/index.svelte | 8 +- .../builder/portal/manage/auth/index.svelte | 114 ++-- .../builder/portal/manage/email/index.svelte | 15 +- .../portal/manage/users/[userId].svelte | 4 +- .../portal/settings/organisation.svelte | 2 +- packages/builder/src/pages/index.svelte | 9 +- packages/builder/src/stores/portal/admin.js | 53 +- packages/builder/src/stores/portal/auth.js | 110 +--- packages/builder/src/stores/portal/email.js | 6 +- packages/builder/src/stores/portal/oidc.js | 10 +- .../builder/src/stores/portal/organisation.js | 8 +- packages/builder/src/stores/portal/users.js | 12 +- packages/client/src/api/auth.js | 4 +- packages/server/__mocks__/node-fetch.ts | 2 +- packages/server/package.json | 7 +- packages/server/scripts/dev/manage.js | 41 +- .../scripts/integrations/postgres/init.sql | 11 +- packages/server/scripts/multiTenancy.js | 8 - .../server/src/api/controllers/apikeys.js | 30 +- .../server/src/api/controllers/application.js | 14 +- packages/server/src/api/controllers/auth.js | 2 +- .../src/api/controllers/deploy/index.js | 23 +- packages/server/src/api/controllers/dev.js | 3 +- .../src/api/controllers/row/internal.js | 10 +- packages/server/src/api/controllers/user.js | 2 +- packages/server/src/api/index.js | 12 +- packages/server/src/api/routes/application.js | 2 +- packages/server/src/api/routes/dev.js | 6 +- .../server/src/api/routes/tests/row.spec.js | 2 +- .../routes/tests/utilities/TestFunctions.js | 5 +- .../server/src/automations/steps/serverLog.js | 2 +- packages/server/src/automations/thread.js | 31 +- packages/server/src/db/builder.js | 38 ++ packages/server/src/db/linkedRows/index.js | 11 +- packages/server/src/db/utils.js | 21 +- packages/server/src/environment.js | 1 - packages/server/src/middleware/currentapp.js | 1 - .../src/tests/utilities/TestConfiguration.js | 19 +- .../server/src/tests/utilities/structures.js | 2 - packages/server/src/utilities/global.js | 13 +- packages/server/src/utilities/index.js | 2 + packages/server/src/utilities/redis.js | 6 +- packages/server/src/utilities/rowProcessor.js | 7 +- packages/server/src/utilities/users.js | 2 +- .../server/src/utilities/workerRequests.js | 28 +- packages/server/yarn.lock | 104 ++- packages/worker/package.json | 7 +- packages/worker/scripts/dev/manage.js | 40 +- packages/worker/scripts/jestSetup.js | 1 - packages/worker/scripts/multiTenancy.js | 8 - .../api/controllers/{global => admin}/auth.js | 46 +- .../controllers/{global => admin}/configs.js | 81 +-- .../controllers/{global => admin}/email.js | 11 +- .../{global/workspaces.js => admin/groups.js} | 29 +- .../controllers/{global => admin}/roles.js | 3 +- .../controllers/{global => admin}/sessions.js | 0 .../{global => admin}/templates.js | 10 +- .../controllers/{global => admin}/users.js | 147 ++--- packages/worker/src/api/controllers/app.js | 12 +- .../src/api/controllers/system/flags.js | 7 - .../src/api/controllers/system/tenants.js | 33 - packages/worker/src/api/index.js | 63 +- packages/worker/src/api/routes/admin/auth.js | 45 ++ .../api/routes/{global => admin}/configs.js | 23 +- .../src/api/routes/{global => admin}/email.js | 8 +- .../{global/workspaces.js => admin/groups.js} | 14 +- packages/worker/src/api/routes/admin/roles.js | 11 + .../worker/src/api/routes/admin/sessions.js | 14 + .../api/routes/{global => admin}/templates.js | 16 +- .../src/api/routes/{global => admin}/users.js | 30 +- packages/worker/src/api/routes/global/auth.js | 81 --- .../worker/src/api/routes/global/roles.js | 11 - .../worker/src/api/routes/global/sessions.js | 14 - packages/worker/src/api/routes/index.js | 22 +- .../worker/src/api/routes/system/flags.js | 8 - .../worker/src/api/routes/system/tenants.js | 11 - .../worker/src/api/routes/tests/auth.spec.js | 23 +- .../src/api/routes/tests/configs.spec.js | 9 +- .../worker/src/api/routes/tests/email.spec.js | 6 +- .../src/api/routes/tests/realEmail.spec.js | 4 +- .../worker/src/api/routes/tests/users.spec.js | 9 +- .../tests/utilities/TestConfiguration.js | 23 +- .../api/routes/tests/utilities/controllers.js | 10 +- .../api/routes/tests/utilities/structures.js | 1 - packages/worker/src/constants/index.js | 4 + .../worker/src/constants/templates/index.js | 6 +- packages/worker/src/environment.js | 5 +- packages/worker/src/utilities/email.js | 33 +- packages/worker/src/utilities/redis.js | 6 +- packages/worker/src/utilities/templates.js | 14 +- packages/worker/yarn.lock | 617 +----------------- 127 files changed, 881 insertions(+), 2359 deletions(-) delete mode 100644 packages/auth/src/db/constants.js delete mode 100644 packages/auth/src/middleware/matchers.js delete mode 100644 packages/auth/src/middleware/tenancy.js delete mode 100644 packages/auth/src/tenancy/FunctionContext.js delete mode 100644 packages/auth/src/tenancy/context.js delete mode 100644 packages/auth/src/tenancy/index.js delete mode 100644 packages/auth/src/tenancy/tenancy.js delete mode 100644 packages/auth/tenancy.js delete mode 100644 packages/builder/src/pages/builder/auth/org.svelte delete mode 100644 packages/server/scripts/multiTenancy.js create mode 100644 packages/server/src/db/builder.js delete mode 100644 packages/worker/scripts/multiTenancy.js rename packages/worker/src/api/controllers/{global => admin}/auth.js (82%) rename packages/worker/src/api/controllers/{global => admin}/configs.js (74%) rename packages/worker/src/api/controllers/{global => admin}/email.js (58%) rename packages/worker/src/api/controllers/{global/workspaces.js => admin/groups.js} (53%) rename packages/worker/src/api/controllers/{global => admin}/roles.js (90%) rename packages/worker/src/api/controllers/{global => admin}/sessions.js (100%) rename packages/worker/src/api/controllers/{global => admin}/templates.js (86%) rename packages/worker/src/api/controllers/{global => admin}/users.js (64%) delete mode 100644 packages/worker/src/api/controllers/system/flags.js delete mode 100644 packages/worker/src/api/controllers/system/tenants.js create mode 100644 packages/worker/src/api/routes/admin/auth.js rename packages/worker/src/api/routes/{global => admin}/configs.js (82%) rename packages/worker/src/api/routes/{global => admin}/email.js (80%) rename packages/worker/src/api/routes/{global/workspaces.js => admin/groups.js} (68%) create mode 100644 packages/worker/src/api/routes/admin/roles.js create mode 100644 packages/worker/src/api/routes/admin/sessions.js rename packages/worker/src/api/routes/{global => admin}/templates.js (66%) rename packages/worker/src/api/routes/{global => admin}/users.js (71%) delete mode 100644 packages/worker/src/api/routes/global/auth.js delete mode 100644 packages/worker/src/api/routes/global/roles.js delete mode 100644 packages/worker/src/api/routes/global/sessions.js delete mode 100644 packages/worker/src/api/routes/system/flags.js delete mode 100644 packages/worker/src/api/routes/system/tenants.js delete mode 100644 packages/worker/src/api/routes/tests/utilities/structures.js diff --git a/hosting/envoy.dev.yaml.hbs b/hosting/envoy.dev.yaml.hbs index 01d5a09efa..76417b3e0d 100644 --- a/hosting/envoy.dev.yaml.hbs +++ b/hosting/envoy.dev.yaml.hbs @@ -26,18 +26,10 @@ static_resources: cluster: couchdb-service prefix_rewrite: "/" - - match: { prefix: "/api/system/" } - route: - cluster: worker-dev - - match: { prefix: "/api/admin/" } route: cluster: worker-dev - - match: { prefix: "/api/global/" } - route: - cluster: worker-dev - - match: { prefix: "/api/" } route: cluster: server-dev diff --git a/hosting/envoy.yaml b/hosting/envoy.yaml index d5f9ebee28..d7b34f4d5e 100644 --- a/hosting/envoy.yaml +++ b/hosting/envoy.yaml @@ -37,19 +37,11 @@ static_resources: route: cluster: app-service - # special cases for worker admin (deprecated), global and system API - - match: { prefix: "/api/global/" } - route: - cluster: worker-service - + # special case for worker admin API - match: { prefix: "/api/admin/" } route: cluster: worker-service - - match: { prefix: "/api/system/" } - route: - cluster: worker-service - - match: { path: "/" } route: cluster: app-service diff --git a/package.json b/package.json index 8fbed6175a..4f545d935f 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,6 @@ "test:e2e": "lerna run cy:test", "test:e2e:ci": "lerna run cy:ci", "build:docker": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -", - "build:docker:develop": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -", - "multi:enable": "lerna run multi:enable", - "multi:disable": "lerna run multi:disable" + "build:docker:develop": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -" } } diff --git a/packages/auth/db.js b/packages/auth/db.js index a7b38821a7..4b03ec36cc 100644 --- a/packages/auth/db.js +++ b/packages/auth/db.js @@ -1,4 +1 @@ -module.exports = { - ...require("./src/db/utils"), - ...require("./src/db/constants"), -} +module.exports = require("./src/db/utils") diff --git a/packages/auth/package.json b/packages/auth/package.json index fe716d2469..9261078561 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -13,7 +13,6 @@ "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.901.0", "bcryptjs": "^2.4.3", - "cls-hooked": "^4.2.2", "ioredis": "^4.27.1", "jsonwebtoken": "^8.5.1", "koa-passport": "^4.1.4", diff --git a/packages/auth/src/cache/user.js b/packages/auth/src/cache/user.js index 4a19da489f..46202cbfe9 100644 --- a/packages/auth/src/cache/user.js +++ b/packages/auth/src/cache/user.js @@ -1,21 +1,15 @@ +const { getDB } = require("../db") +const { StaticDatabases } = require("../db/utils") const redis = require("../redis/authRedis") -const { getTenantId, lookupTenantId, getGlobalDB } = require("../tenancy") const EXPIRY_SECONDS = 3600 -exports.getUser = async (userId, tenantId = null) => { - if (!tenantId) { - try { - tenantId = getTenantId() - } catch (err) { - tenantId = await lookupTenantId(userId) - } - } +exports.getUser = async userId => { const client = await redis.getUserClient() // try cache let user = await client.get(userId) if (!user) { - user = await getGlobalDB(tenantId).get(userId) + user = await getDB(StaticDatabases.GLOBAL.name).get(userId) client.store(userId, user, EXPIRY_SECONDS) } return user diff --git a/packages/auth/src/constants.js b/packages/auth/src/constants.js index 4b4aef5a42..c8cc34e937 100644 --- a/packages/auth/src/constants.js +++ b/packages/auth/src/constants.js @@ -14,14 +14,13 @@ exports.Headers = { API_VER: "x-budibase-api-version", APP_ID: "x-budibase-app-id", TYPE: "x-budibase-type", - TENANT_ID: "x-budibase-tenant-id", } exports.GlobalRoles = { OWNER: "owner", ADMIN: "admin", BUILDER: "builder", - WORKSPACE_MANAGER: "workspace_manager", + GROUP_MANAGER: "group_manager", } exports.Configs = { @@ -32,5 +31,3 @@ exports.Configs = { OIDC: "oidc", OIDC_LOGOS: "logos_oidc", } - -exports.DEFAULT_TENANT_ID = "default" diff --git a/packages/auth/src/db/constants.js b/packages/auth/src/db/constants.js deleted file mode 100644 index 77643ce4c5..0000000000 --- a/packages/auth/src/db/constants.js +++ /dev/null @@ -1,17 +0,0 @@ -exports.SEPARATOR = "_" - -exports.StaticDatabases = { - GLOBAL: { - name: "global-db", - docs: { - apiKeys: "apikeys", - }, - }, - // contains information about tenancy and so on - PLATFORM_INFO: { - name: "global-info", - docs: { - tenants: "tenants", - }, - }, -} diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 7d3a69ccd7..100dc005c8 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -1,36 +1,36 @@ const { newid } = require("../hashing") const Replication = require("./Replication") -const { DEFAULT_TENANT_ID } = require("../constants") -const env = require("../environment") -const { StaticDatabases, SEPARATOR } = require("./constants") -const { getTenantId } = require("../tenancy") const UNICODE_MAX = "\ufff0" +const SEPARATOR = "_" exports.ViewNames = { USER_BY_EMAIL: "by_email", } -exports.StaticDatabases = StaticDatabases - -const PRE_APP = "app" -const PRE_DEV = "dev" +exports.StaticDatabases = { + GLOBAL: { + name: "global-db", + }, + DEPLOYMENTS: { + name: "deployments", + }, +} const DocumentTypes = { USER: "us", - WORKSPACE: "workspace", + GROUP: "group", CONFIG: "config", TEMPLATE: "template", - APP: PRE_APP, - DEV: PRE_DEV, - APP_DEV: `${PRE_APP}${SEPARATOR}${PRE_DEV}`, - APP_METADATA: `${PRE_APP}${SEPARATOR}metadata`, + APP: "app", + APP_DEV: "app_dev", + APP_METADATA: "app_metadata", ROLE: "role", } exports.DocumentTypes = DocumentTypes exports.APP_PREFIX = DocumentTypes.APP + SEPARATOR -exports.APP_DEV = exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR +exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR exports.SEPARATOR = SEPARATOR function isDevApp(app) { @@ -61,21 +61,21 @@ function getDocParams(docType, docId = null, otherProps = {}) { } /** - * Generates a new workspace ID. - * @returns {string} The new workspace ID which the workspace doc can be stored under. + * Generates a new group ID. + * @returns {string} The new group ID which the group doc can be stored under. */ -exports.generateWorkspaceID = () => { - return `${DocumentTypes.WORKSPACE}${SEPARATOR}${newid()}` +exports.generateGroupID = () => { + return `${DocumentTypes.GROUP}${SEPARATOR}${newid()}` } /** - * Gets parameters for retrieving workspaces. + * Gets parameters for retrieving groups. */ -exports.getWorkspaceParams = (id = "", otherProps = {}) => { +exports.getGroupParams = (id = "", otherProps = {}) => { return { ...otherProps, - startkey: `${DocumentTypes.WORKSPACE}${SEPARATOR}${id}`, - endkey: `${DocumentTypes.WORKSPACE}${SEPARATOR}${id}${UNICODE_MAX}`, + startkey: `${DocumentTypes.GROUP}${SEPARATOR}${id}`, + endkey: `${DocumentTypes.GROUP}${SEPARATOR}${id}${UNICODE_MAX}`, } } @@ -103,14 +103,14 @@ exports.getGlobalUserParams = (globalId, otherProps = {}) => { /** * Generates a template ID. - * @param ownerId The owner/user of the template, this could be global or a workspace level. + * @param ownerId The owner/user of the template, this could be global or a group level. */ exports.generateTemplateID = ownerId => { return `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}` } /** - * Gets parameters for retrieving templates. Owner ID must be specified, either global or a workspace level. + * Gets parameters for retrieving templates. Owner ID must be specified, either global or a group level. */ exports.getTemplateParams = (ownerId, templateId, otherProps = {}) => { if (!templateId) { @@ -163,26 +163,11 @@ exports.getDeployedAppID = appId => { * different users/companies apps as there is no security around it - all apps are returned. * @return {Promise} returns the app information document stored in each app database. */ -exports.getAllApps = async (CouchDB, { dev, all } = {}) => { - let tenantId = getTenantId() - if (!env.MULTI_TENANCY && !tenantId) { - tenantId = DEFAULT_TENANT_ID - } +exports.getAllApps = async ({ CouchDB, dev, all } = {}) => { let allDbs = await CouchDB.allDbs() - const appDbNames = allDbs.filter(dbName => { - const split = dbName.split(SEPARATOR) - // it is an app, check the tenantId - if (split[0] === DocumentTypes.APP) { - const noTenantId = split.length === 2 || split[1] === DocumentTypes.DEV - // tenantId is always right before the UUID - const possibleTenantId = split[split.length - 2] - return ( - (tenantId === DEFAULT_TENANT_ID && noTenantId) || - possibleTenantId === tenantId - ) - } - return false - }) + const appDbNames = allDbs.filter(dbName => + dbName.startsWith(exports.APP_PREFIX) + ) const appPromises = appDbNames.map(db => // skip setup otherwise databases could be re-created new CouchDB(db, { skip_setup: true }).get(DocumentTypes.APP_METADATA) @@ -229,8 +214,8 @@ exports.dbExists = async (CouchDB, dbName) => { * Generates a new configuration ID. * @returns {string} The new configuration ID which the config doc can be stored under. */ -const generateConfigID = ({ type, workspace, user }) => { - const scope = [type, workspace, user].filter(Boolean).join(SEPARATOR) +const generateConfigID = ({ type, group, user }) => { + const scope = [type, group, user].filter(Boolean).join(SEPARATOR) return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}` } @@ -238,8 +223,8 @@ const generateConfigID = ({ type, workspace, user }) => { /** * Gets parameters for retrieving configurations. */ -const getConfigParams = ({ type, workspace, user }, otherProps = {}) => { - const scope = [type, workspace, user].filter(Boolean).join(SEPARATOR) +const getConfigParams = ({ type, group, user }, otherProps = {}) => { + const scope = [type, group, user].filter(Boolean).join(SEPARATOR) return { ...otherProps, @@ -249,15 +234,15 @@ const getConfigParams = ({ type, workspace, user }, otherProps = {}) => { } /** - * Returns the most granular configuration document from the DB based on the type, workspace and userID passed. + * Returns the most granular configuration document from the DB based on the type, group and userID passed. * @param {Object} db - db instance to query - * @param {Object} scopes - the type, workspace and userID scopes of the configuration. + * @param {Object} scopes - the type, group and userID scopes of the configuration. * @returns The most granular configuration document based on the scope. */ -const getScopedFullConfig = async function (db, { type, user, workspace }) { +const getScopedFullConfig = async function (db, { type, user, group }) { const response = await db.allDocs( getConfigParams( - { type, user, workspace }, + { type, user, group }, { include_docs: true, } @@ -267,14 +252,14 @@ const getScopedFullConfig = async function (db, { type, user, workspace }) { function determineScore(row) { const config = row.doc - // Config is specific to a user and a workspace - if (config._id.includes(generateConfigID({ type, user, workspace }))) { + // Config is specific to a user and a group + if (config._id.includes(generateConfigID({ type, user, group }))) { return 4 } else if (config._id.includes(generateConfigID({ type, user }))) { // Config is specific to a user only return 3 - } else if (config._id.includes(generateConfigID({ type, workspace }))) { - // Config is specific to a workspace only + } else if (config._id.includes(generateConfigID({ type, group }))) { + // Config is specific to a group only return 2 } else if (config._id.includes(generateConfigID({ type }))) { // Config is specific to a type only diff --git a/packages/auth/src/db/views.js b/packages/auth/src/db/views.js index 1b48786e24..1f1f28b917 100644 --- a/packages/auth/src/db/views.js +++ b/packages/auth/src/db/views.js @@ -1,4 +1,5 @@ -const { DocumentTypes, ViewNames } = require("./utils") +const { DocumentTypes, ViewNames, StaticDatabases } = require("./utils") +const { getDB } = require("./index") function DesignDoc() { return { @@ -9,7 +10,8 @@ function DesignDoc() { } } -exports.createUserEmailView = async db => { +exports.createUserEmailView = async () => { + const db = getDB(StaticDatabases.GLOBAL.name) let designDoc try { designDoc = await db.get("_design/database") diff --git a/packages/auth/src/environment.js b/packages/auth/src/environment.js index e12918f3ac..355843d02d 100644 --- a/packages/auth/src/environment.js +++ b/packages/auth/src/environment.js @@ -16,7 +16,6 @@ module.exports = { MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, MINIO_URL: process.env.MINIO_URL, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, - MULTI_TENANCY: process.env.MULTI_TENANCY, isTest, _set(key, value) { process.env[key] = value diff --git a/packages/auth/src/index.js b/packages/auth/src/index.js index 5421dea214..98c558706a 100644 --- a/packages/auth/src/index.js +++ b/packages/auth/src/index.js @@ -2,7 +2,6 @@ const passport = require("koa-passport") const LocalStrategy = require("passport-local").Strategy const JwtStrategy = require("passport-jwt").Strategy const { StaticDatabases } = require("./db/utils") -const { getGlobalDB } = require("./tenancy") const { jwt, local, @@ -10,9 +9,8 @@ const { google, oidc, auditLog, - tenancy, } = require("./middleware") -const { setDB } = require("./db") +const { setDB, getDB } = require("./db") const userCache = require("./cache/user") // Strategies @@ -22,7 +20,7 @@ passport.use(new JwtStrategy(jwt.options, jwt.authenticate)) passport.serializeUser((user, done) => done(null, user)) passport.deserializeUser(async (user, done) => { - const db = getGlobalDB() + const db = getDB(StaticDatabases.GLOBAL.name) try { const user = await db.get(user._id) @@ -56,7 +54,6 @@ module.exports = { google, oidc, jwt: require("jsonwebtoken"), - buildTenancyMiddleware: tenancy, auditLog, }, cache: { diff --git a/packages/auth/src/middleware/authenticated.js b/packages/auth/src/middleware/authenticated.js index 303553212b..647ec659f5 100644 --- a/packages/auth/src/middleware/authenticated.js +++ b/packages/auth/src/middleware/authenticated.js @@ -2,34 +2,46 @@ const { Cookies, Headers } = require("../constants") const { getCookie, clearCookie } = require("../utils") const { getUser } = require("../cache/user") const { getSession, updateSessionTTL } = require("../security/sessions") -const { buildMatcherRegex, matches } = require("./matchers") const env = require("../environment") -function finalise( - ctx, - { authenticated, user, internal, version, publicEndpoint } = {} -) { - ctx.publicEndpoint = publicEndpoint || false +const PARAM_REGEX = /\/:(.*?)\//g + +function buildNoAuthRegex(patterns) { + return patterns.map(pattern => { + const isObj = typeof pattern === "object" && pattern.route + const method = isObj ? pattern.method : "GET" + let route = isObj ? pattern.route : pattern + + const matches = route.match(PARAM_REGEX) + if (matches) { + for (let match of matches) { + route = route.replace(match, "/.*/") + } + } + return { regex: new RegExp(route), method } + }) +} + +function finalise(ctx, { authenticated, user, internal, version } = {}) { ctx.isAuthenticated = authenticated || false ctx.user = user ctx.internal = internal || false ctx.version = version } -/** - * This middleware is tenancy aware, so that it does not depend on other middlewares being used. - * The tenancy modules should not be used here and it should be assumed that the tenancy context - * has not yet been populated. - */ -module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { - const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : [] +module.exports = (noAuthPatterns = [], opts) => { + const noAuthOptions = noAuthPatterns ? buildNoAuthRegex(noAuthPatterns) : [] return async (ctx, next) => { - let publicEndpoint = false const version = ctx.request.headers[Headers.API_VER] // the path is not authenticated - const found = matches(ctx, noAuthOptions) - if (found) { - publicEndpoint = true + const found = noAuthOptions.find(({ regex, method }) => { + return ( + regex.test(ctx.request.url) && + ctx.request.method.toLowerCase() === method.toLowerCase() + ) + }) + if (found != null) { + return next() } try { // check the actual user is authenticated first @@ -46,7 +58,7 @@ module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { error = "No session found" } else { try { - user = await getUser(userId, session.tenantId) + user = await getUser(userId) delete user.password authenticated = true } catch (err) { @@ -62,26 +74,22 @@ module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { } } const apiKey = ctx.request.headers[Headers.API_KEY] - const tenantId = ctx.request.headers[Headers.TENANT_ID] // this is an internal request, no user made it if (!authenticated && apiKey && apiKey === env.INTERNAL_API_KEY) { authenticated = true internal = true } - if (!user && tenantId) { - user = { tenantId } - } // be explicit if (authenticated !== true) { authenticated = false } // isAuthenticated is a function, so use a variable to be able to check authed state - finalise(ctx, { authenticated, user, internal, version, publicEndpoint }) + finalise(ctx, { authenticated, user, internal, version }) return next() } catch (err) { // allow configuring for public access - if ((opts && opts.publicAllowed) || publicEndpoint) { - finalise(ctx, { authenticated: false, version, publicEndpoint }) + if (opts && opts.publicAllowed) { + finalise(ctx, { authenticated: false, version }) } else { ctx.throw(err.status || 403, err) } diff --git a/packages/auth/src/middleware/index.js b/packages/auth/src/middleware/index.js index 689859a139..35c7d9c388 100644 --- a/packages/auth/src/middleware/index.js +++ b/packages/auth/src/middleware/index.js @@ -4,7 +4,6 @@ const google = require("./passport/google") const oidc = require("./passport/oidc") const authenticated = require("./authenticated") const auditLog = require("./auditLog") -const tenancy = require("./tenancy") module.exports = { google, @@ -13,5 +12,4 @@ module.exports = { local, authenticated, auditLog, - tenancy, } diff --git a/packages/auth/src/middleware/matchers.js b/packages/auth/src/middleware/matchers.js deleted file mode 100644 index a555823136..0000000000 --- a/packages/auth/src/middleware/matchers.js +++ /dev/null @@ -1,33 +0,0 @@ -const PARAM_REGEX = /\/:(.*?)(\/.*)?$/g - -exports.buildMatcherRegex = patterns => { - if (!patterns) { - return [] - } - return patterns.map(pattern => { - const isObj = typeof pattern === "object" && pattern.route - const method = isObj ? pattern.method : "GET" - let route = isObj ? pattern.route : pattern - - const matches = route.match(PARAM_REGEX) - if (matches) { - for (let match of matches) { - const pattern = "/.*" + (match.endsWith("/") ? "/" : "") - route = route.replace(match, pattern) - } - } - return { regex: new RegExp(route), method } - }) -} - -exports.matches = (ctx, options) => { - return options.find(({ regex, method }) => { - const urlMatch = regex.test(ctx.request.url) - const methodMatch = - method === "ALL" - ? true - : ctx.request.method.toLowerCase() === method.toLowerCase() - - return urlMatch && methodMatch - }) -} diff --git a/packages/auth/src/middleware/passport/google.js b/packages/auth/src/middleware/passport/google.js index 07d6816c0b..68fe885512 100644 --- a/packages/auth/src/middleware/passport/google.js +++ b/packages/auth/src/middleware/passport/google.js @@ -27,13 +27,13 @@ async function authenticate(accessToken, refreshToken, profile, done) { * from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport. * @returns Dynamically configured Passport Google Strategy */ -exports.strategyFactory = async function (config, callbackUrl) { +exports.strategyFactory = async function (config) { try { - const { clientID, clientSecret } = config + const { clientID, clientSecret, callbackURL } = config - if (!clientID || !clientSecret) { + if (!clientID || !clientSecret || !callbackURL) { throw new Error( - "Configuration invalid. Must contain google clientID and clientSecret" + "Configuration invalid. Must contain google clientID, clientSecret and callbackURL" ) } @@ -41,7 +41,7 @@ exports.strategyFactory = async function (config, callbackUrl) { { clientID: config.clientID, clientSecret: config.clientSecret, - callbackURL: callbackUrl, + callbackURL: config.callbackURL, }, authenticate ) diff --git a/packages/auth/src/middleware/passport/local.js b/packages/auth/src/middleware/passport/local.js index 0db40d64eb..16b53bf894 100644 --- a/packages/auth/src/middleware/passport/local.js +++ b/packages/auth/src/middleware/passport/local.js @@ -6,23 +6,19 @@ const { getGlobalUserByEmail } = require("../../utils") const { authError } = require("./utils") const { newid } = require("../../hashing") const { createASession } = require("../../security/sessions") -const { getTenantId } = require("../../tenancy") const INVALID_ERR = "Invalid Credentials" -exports.options = { - passReqToCallback: true, -} +exports.options = {} /** * Passport Local Authentication Middleware. - * @param {*} ctx the request structure - * @param {*} email username to login with - * @param {*} password plain text password to log in with - * @param {*} done callback from passport to return user information and errors + * @param {*} email - username to login with + * @param {*} password - plain text password to log in with + * @param {*} done - callback from passport to return user information and errors * @returns The authenticated user, or errors if they occur */ -exports.authenticate = async function (ctx, email, password, done) { +exports.authenticate = async function (email, password, done) { if (!email) return authError(done, "Email Required") if (!password) return authError(done, "Password Required") @@ -39,14 +35,12 @@ exports.authenticate = async function (ctx, email, password, done) { // authenticate if (await compare(password, dbUser.password)) { const sessionId = newid() - const tenantId = getTenantId() - await createASession(dbUser._id, { sessionId, tenantId }) + await createASession(dbUser._id, sessionId) dbUser.token = jwt.sign( { userId: dbUser._id, sessionId, - tenantId, }, env.JWT_SECRET ) diff --git a/packages/auth/src/middleware/passport/tests/google.spec.js b/packages/auth/src/middleware/passport/tests/google.spec.js index 9cc878bba9..30e582a68f 100644 --- a/packages/auth/src/middleware/passport/tests/google.spec.js +++ b/packages/auth/src/middleware/passport/tests/google.spec.js @@ -2,9 +2,8 @@ const { data } = require("./utilities/mock-data") -const TENANT_ID = "default" - const googleConfig = { + callbackURL: "http://somecallbackurl", clientID: data.clientID, clientSecret: data.clientSecret, } @@ -27,14 +26,13 @@ describe("google", () => { it("should create successfully create a google strategy", async () => { const google = require("../google") - - const callbackUrl = `/api/global/auth/${TENANT_ID}/google/callback` - await google.strategyFactory(googleConfig, callbackUrl) + + await google.strategyFactory(googleConfig) const expectedOptions = { clientID: googleConfig.clientID, clientSecret: googleConfig.clientSecret, - callbackURL: callbackUrl, + callbackURL: googleConfig.callbackURL, } expect(mockStrategy).toHaveBeenCalledWith( diff --git a/packages/auth/src/middleware/passport/third-party-common.js b/packages/auth/src/middleware/passport/third-party-common.js index 7490cc4031..2ab2816391 100644 --- a/packages/auth/src/middleware/passport/third-party-common.js +++ b/packages/auth/src/middleware/passport/third-party-common.js @@ -1,11 +1,11 @@ const env = require("../../environment") const jwt = require("jsonwebtoken") -const { generateGlobalUserID } = require("../../db/utils") +const database = require("../../db") +const { StaticDatabases, generateGlobalUserID } = require("../../db/utils") const { authError } = require("./utils") const { newid } = require("../../hashing") const { createASession } = require("../../security/sessions") const { getGlobalUserByEmail } = require("../../utils") -const { getGlobalDB, getTenantId } = require("../../tenancy") /** * Common authentication logic for third parties. e.g. OAuth, OIDC. @@ -15,21 +15,19 @@ exports.authenticateThirdParty = async function ( requireLocalAccount = true, done ) { - if (!thirdPartyUser.provider) { + if (!thirdPartyUser.provider) return authError(done, "third party user provider required") - } - if (!thirdPartyUser.userId) { + if (!thirdPartyUser.userId) return authError(done, "third party user id required") - } - if (!thirdPartyUser.email) { + if (!thirdPartyUser.email) return authError(done, "third party user email required") - } + + const db = database.getDB(StaticDatabases.GLOBAL.name) + + let dbUser // use the third party id const userId = generateGlobalUserID(thirdPartyUser.userId) - const db = getGlobalDB() - - let dbUser // try to load by id try { @@ -75,8 +73,7 @@ exports.authenticateThirdParty = async function ( // authenticate const sessionId = newid() - const tenantId = getTenantId() - await createASession(dbUser._id, { sessionId, tenantId }) + await createASession(dbUser._id, sessionId) dbUser.token = jwt.sign( { diff --git a/packages/auth/src/middleware/tenancy.js b/packages/auth/src/middleware/tenancy.js deleted file mode 100644 index b80b9a6763..0000000000 --- a/packages/auth/src/middleware/tenancy.js +++ /dev/null @@ -1,14 +0,0 @@ -const { setTenantId } = require("../tenancy") -const ContextFactory = require("../tenancy/FunctionContext") -const { buildMatcherRegex, matches } = require("./matchers") - -module.exports = (allowQueryStringPatterns, noTenancyPatterns) => { - const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) - const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) - - return ContextFactory.getMiddleware(ctx => { - const allowNoTenant = !!matches(ctx, noTenancyOptions) - const allowQs = !!matches(ctx, allowQsOptions) - setTenantId(ctx, { allowQs, allowNoTenant }) - }) -} diff --git a/packages/auth/src/security/sessions.js b/packages/auth/src/security/sessions.js index 328f74c794..4051df7123 100644 --- a/packages/auth/src/security/sessions.js +++ b/packages/auth/src/security/sessions.js @@ -12,13 +12,12 @@ function makeSessionID(userId, sessionId) { return `${userId}/${sessionId}` } -exports.createASession = async (userId, session) => { +exports.createASession = async (userId, sessionId) => { const client = await redis.getSessionClient() - const sessionId = session.sessionId - session = { + const session = { createdAt: new Date().toISOString(), lastAccessedAt: new Date().toISOString(), - ...session, + sessionId, userId, } await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS) diff --git a/packages/auth/src/tenancy/FunctionContext.js b/packages/auth/src/tenancy/FunctionContext.js deleted file mode 100644 index d97a3a30b4..0000000000 --- a/packages/auth/src/tenancy/FunctionContext.js +++ /dev/null @@ -1,73 +0,0 @@ -const cls = require("cls-hooked") -const { newid } = require("../hashing") - -const REQUEST_ID_KEY = "requestId" - -class FunctionContext { - static getMiddleware(updateCtxFn = null) { - const namespace = this.createNamespace() - - return async function (ctx, next) { - await new Promise( - namespace.bind(function (resolve, reject) { - // store a contextual request ID that can be used anywhere (audit logs) - namespace.set(REQUEST_ID_KEY, newid()) - namespace.bindEmitter(ctx.req) - namespace.bindEmitter(ctx.res) - - if (updateCtxFn) { - updateCtxFn(ctx) - } - next().then(resolve).catch(reject) - }) - ) - } - } - - static run(callback) { - const namespace = this.createNamespace() - - return namespace.runAndReturn(callback) - } - - static setOnContext(key, value) { - const namespace = this.createNamespace() - namespace.set(key, value) - } - - static getContextStorage() { - if (this._namespace && this._namespace.active) { - let contextData = this._namespace.active - delete contextData.id - delete contextData._ns_name - return contextData - } - - return {} - } - - static getFromContext(key) { - const context = this.getContextStorage() - if (context) { - return context[key] - } else { - return null - } - } - - static destroyNamespace() { - if (this._namespace) { - cls.destroyNamespace("session") - this._namespace = null - } - } - - static createNamespace() { - if (!this._namespace) { - this._namespace = cls.createNamespace("session") - } - return this._namespace - } -} - -module.exports = FunctionContext diff --git a/packages/auth/src/tenancy/context.js b/packages/auth/src/tenancy/context.js deleted file mode 100644 index f3f1f541e9..0000000000 --- a/packages/auth/src/tenancy/context.js +++ /dev/null @@ -1,81 +0,0 @@ -const env = require("../environment") -const { Headers } = require("../../constants") -const cls = require("./FunctionContext") - -exports.DEFAULT_TENANT_ID = "default" - -exports.isDefaultTenant = () => { - return exports.getTenantId() === exports.DEFAULT_TENANT_ID -} - -exports.isMultiTenant = () => { - return env.MULTI_TENANCY -} - -const TENANT_ID = "tenantId" - -// used for automations, API endpoints should always be in context already -exports.doInTenant = (tenantId, task) => { - return cls.run(() => { - // set the tenant id - cls.setOnContext(TENANT_ID, tenantId) - - // invoke the task - const result = task() - - return result - }) -} - -exports.updateTenantId = tenantId => { - cls.setOnContext(TENANT_ID, tenantId) -} - -exports.setTenantId = ( - ctx, - opts = { allowQs: false, allowNoTenant: false } -) => { - let tenantId - // exit early if not multi-tenant - if (!exports.isMultiTenant()) { - cls.setOnContext(TENANT_ID, this.DEFAULT_TENANT_ID) - return - } - - const allowQs = opts && opts.allowQs - const allowNoTenant = opts && opts.allowNoTenant - const header = ctx.request.headers[Headers.TENANT_ID] - const user = ctx.user || {} - if (allowQs) { - const query = ctx.request.query || {} - tenantId = query.tenantId - } - // override query string (if allowed) by user, or header - // URL params cannot be used in a middleware, as they are - // processed later in the chain - tenantId = user.tenantId || header || tenantId - - if (!tenantId && !allowNoTenant) { - ctx.throw(403, "Tenant id not set") - } - // check tenant ID just incase no tenant was allowed - if (tenantId) { - cls.setOnContext(TENANT_ID, tenantId) - } -} - -exports.isTenantIdSet = () => { - const tenantId = cls.getFromContext(TENANT_ID) - return !!tenantId -} - -exports.getTenantId = () => { - if (!exports.isMultiTenant()) { - return exports.DEFAULT_TENANT_ID - } - const tenantId = cls.getFromContext(TENANT_ID) - if (!tenantId) { - throw Error("Tenant id not found") - } - return tenantId -} diff --git a/packages/auth/src/tenancy/index.js b/packages/auth/src/tenancy/index.js deleted file mode 100644 index 2fe257d885..0000000000 --- a/packages/auth/src/tenancy/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - ...require("./context"), - ...require("./tenancy"), -} diff --git a/packages/auth/src/tenancy/tenancy.js b/packages/auth/src/tenancy/tenancy.js deleted file mode 100644 index 6e18ea7154..0000000000 --- a/packages/auth/src/tenancy/tenancy.js +++ /dev/null @@ -1,105 +0,0 @@ -const { getDB } = require("../db") -const { SEPARATOR, StaticDatabases } = require("../db/constants") -const { getTenantId, DEFAULT_TENANT_ID, isMultiTenant } = require("./context") -const env = require("../environment") - -const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants -const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name - -exports.addTenantToUrl = url => { - const tenantId = getTenantId() - - if (isMultiTenant()) { - const char = url.indexOf("?") === -1 ? "?" : "&" - url += `${char}tenantId=${tenantId}` - } - - return url -} - -exports.doesTenantExist = async tenantId => { - const db = getDB(PLATFORM_INFO_DB) - let tenants - try { - tenants = await db.get(TENANT_DOC) - } catch (err) { - // if theres an error the doc doesn't exist, no tenants exist - return false - } - return ( - tenants && - Array.isArray(tenants.tenantIds) && - tenants.tenantIds.indexOf(tenantId) !== -1 - ) -} - -exports.tryAddTenant = async (tenantId, userId, email) => { - const db = getDB(PLATFORM_INFO_DB) - const getDoc = async id => { - if (!id) { - return null - } - try { - return await db.get(id) - } catch (err) { - return { _id: id } - } - } - let [tenants, userIdDoc, emailDoc] = await Promise.all([ - getDoc(TENANT_DOC), - getDoc(userId), - getDoc(email), - ]) - if (!Array.isArray(tenants.tenantIds)) { - tenants = { - _id: TENANT_DOC, - tenantIds: [], - } - } - let promises = [] - if (userIdDoc) { - userIdDoc.tenantId = tenantId - promises.push(db.put(userIdDoc)) - } - if (emailDoc) { - emailDoc.tenantId = tenantId - promises.push(db.put(emailDoc)) - } - if (tenants.tenantIds.indexOf(tenantId) === -1) { - tenants.tenantIds.push(tenantId) - promises.push(db.put(tenants)) - } - await Promise.all(promises) -} - -exports.getGlobalDB = (tenantId = null) => { - // tenant ID can be set externally, for example user API where - // new tenants are being created, this may be the case - if (!tenantId) { - tenantId = getTenantId() - } - - let dbName - - if (tenantId === DEFAULT_TENANT_ID) { - dbName = StaticDatabases.GLOBAL.name - } else { - dbName = `${tenantId}${SEPARATOR}${StaticDatabases.GLOBAL.name}` - } - - return getDB(dbName) -} - -exports.lookupTenantId = async userId => { - const db = getDB(StaticDatabases.PLATFORM_INFO.name) - let tenantId = env.MULTI_TENANCY ? DEFAULT_TENANT_ID : null - try { - const doc = await db.get(userId) - if (doc && doc.tenantId) { - tenantId = doc.tenantId - } - } catch (err) { - // just return the default - } - return tenantId -} diff --git a/packages/auth/src/utils.js b/packages/auth/src/utils.js index 5936948fd7..6bc1e0e3a6 100644 --- a/packages/auth/src/utils.js +++ b/packages/auth/src/utils.js @@ -1,9 +1,14 @@ -const { DocumentTypes, SEPARATOR, ViewNames } = require("./db/utils") +const { + DocumentTypes, + SEPARATOR, + ViewNames, + StaticDatabases, +} = require("./db/utils") const jwt = require("jsonwebtoken") const { options } = require("./middleware/passport/jwt") const { createUserEmailView } = require("./db/views") +const { getDB } = require("./db") const { Headers } = require("./constants") -const { getGlobalDB } = require("./tenancy") const APP_PREFIX = DocumentTypes.APP + SEPARATOR @@ -106,7 +111,7 @@ exports.getGlobalUserByEmail = async email => { if (email == null) { throw "Must supply an email address to view" } - const db = getGlobalDB() + const db = getDB(StaticDatabases.GLOBAL.name) try { let users = ( await db.query(`database/${ViewNames.USER_BY_EMAIL}`, { @@ -118,7 +123,7 @@ exports.getGlobalUserByEmail = async email => { return users.length <= 1 ? users[0] : users } catch (err) { if (err != null && err.name === "not_found") { - await createUserEmailView(db) + await createUserEmailView() return exports.getGlobalUserByEmail(email) } else { throw err diff --git a/packages/auth/tenancy.js b/packages/auth/tenancy.js deleted file mode 100644 index 9ca808b74e..0000000000 --- a/packages/auth/tenancy.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("./src/tenancy") diff --git a/packages/auth/yarn.lock b/packages/auth/yarn.lock index b6be8ad1e8..8957ecb0fc 100644 --- a/packages/auth/yarn.lock +++ b/packages/auth/yarn.lock @@ -798,13 +798,6 @@ ast-types@0.9.6: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= -async-hook-jl@^1.7.6: - version "1.7.6" - resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68" - integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg== - dependencies: - stack-chain "^1.3.7" - async@~2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" @@ -1151,15 +1144,6 @@ clone-buffer@1.0.0: resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= -cls-hooked@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" - integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw== - dependencies: - async-hook-jl "^1.7.6" - emitter-listener "^1.0.1" - semver "^5.4.1" - cluster-key-slot@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" @@ -1460,13 +1444,6 @@ electron-to-chromium@^1.3.723: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.775.tgz#046517d1f2cea753e06fff549995b9dc45e20082" integrity sha512-EGuiJW4yBPOTj2NtWGZcX93ZE8IGj33HJAx4d3ouE2zOfW2trbWU+t1e0yzLr1qQIw81++txbM3BH52QwSRE6Q== -emitter-listener@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" - integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== - dependencies: - shimmer "^1.2.0" - emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" @@ -4058,7 +4035,7 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -4119,11 +4096,6 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shimmer@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" - integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== - signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -4278,11 +4250,6 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stack-chain@^1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" - integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= - stack-utils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte index b1f6d7e733..43a5d205d9 100644 --- a/packages/builder/src/pages/builder/_layout.svelte +++ b/packages/builder/src/pages/builder/_layout.svelte @@ -4,10 +4,7 @@ import { onMount } from "svelte" let loaded = false - - $: multiTenancyEnabled = $admin.multiTenancy $: hasAdminUser = !!$admin?.checklist?.adminUser - $: tenantSet = $auth.tenantSet onMount(async () => { await admin.init() @@ -15,14 +12,9 @@ loaded = true }) + // Force creation of an admin user if one doesn't exist $: { - const apiReady = $admin.loaded && $auth.loaded - // if tenant is not set go to it - if (loaded && apiReady && multiTenancyEnabled && !tenantSet) { - $redirect("./auth/org") - } - // Force creation of an admin user if one doesn't exist - else if (loaded && apiReady && !hasAdminUser) { + if (loaded && !hasAdminUser) { $redirect("./admin") } } @@ -37,7 +29,7 @@ !$isActive("./invite") ) { const returnUrl = encodeURIComponent(window.location.pathname) - $redirect("./auth?", { returnUrl }) + $redirect("./auth/login?", { returnUrl }) } else if ($auth?.user?.forceResetPassword) { $redirect("./auth/reset") } diff --git a/packages/builder/src/pages/builder/admin/index.svelte b/packages/builder/src/pages/builder/admin/index.svelte index 4d7e39db81..f8cbc21455 100644 --- a/packages/builder/src/pages/builder/admin/index.svelte +++ b/packages/builder/src/pages/builder/admin/index.svelte @@ -6,25 +6,20 @@ Layout, Input, Body, - ActionButton, } from "@budibase/bbui" import { goto } from "@roxi/routify" import api from "builderStore/api" - import { admin, auth } from "stores/portal" + import { admin } from "stores/portal" import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte" import Logo from "assets/bb-emblem.svg" let adminUser = {} let error - $: tenantId = $auth.tenantId - $: multiTenancyEnabled = $admin.multiTenancy - async function save() { try { - adminUser.tenantId = tenantId // Save the admin user - const response = await api.post(`/api/global/users/init`, adminUser) + const response = await api.post(`/api/admin/users/init`, adminUser) const json = await response.json() if (response.status !== 200) { throw new Error(json.message) @@ -52,22 +47,9 @@ - - - {#if multiTenancyEnabled} - { - admin.unload() - $goto("../auth/org") - }} - > - Change organisation - - {/if} - + diff --git a/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte b/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte index 0acaa127cc..c9a6aba218 100644 --- a/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte +++ b/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte @@ -1,18 +1,14 @@ {#if show} - window.open(`/api/global/auth/${tenantId}/google`, "_blank")} + on:click={() => window.open("/api/admin/auth/google", "_blank")} >
google icon diff --git a/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte b/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte index 24aca0c396..22ecad1620 100644 --- a/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte +++ b/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte @@ -31,7 +31,7 @@ {#if show} - window.open(`/api/global/auth/oidc/configs/${$oidc.uuid}`, "_blank")} + window.open(`/api/admin/auth/oidc/configs/${$oidc.uuid}`, "_blank")} >
oidc icon diff --git a/packages/builder/src/pages/builder/auth/forgot.svelte b/packages/builder/src/pages/builder/auth/forgot.svelte index 2503a99eb2..85301b3f02 100644 --- a/packages/builder/src/pages/builder/auth/forgot.svelte +++ b/packages/builder/src/pages/builder/auth/forgot.svelte @@ -6,12 +6,10 @@ Layout, Body, Heading, - ActionButton, } from "@budibase/bbui" import { organisation, auth } from "stores/portal" import Logo from "assets/bb-emblem.svg" import { onMount } from "svelte" - import { goto } from "@roxi/routify" let email = "" @@ -43,12 +41,9 @@ - - - $goto("../")}>Back - +
diff --git a/packages/builder/src/pages/builder/auth/index.svelte b/packages/builder/src/pages/builder/auth/index.svelte index a2a02e65c1..12570aeeb5 100644 --- a/packages/builder/src/pages/builder/auth/index.svelte +++ b/packages/builder/src/pages/builder/auth/index.svelte @@ -1,24 +1,4 @@ diff --git a/packages/builder/src/pages/builder/auth/login.svelte b/packages/builder/src/pages/builder/auth/login.svelte index 783e5a4903..8ce27b98bb 100644 --- a/packages/builder/src/pages/builder/auth/login.svelte +++ b/packages/builder/src/pages/builder/auth/login.svelte @@ -10,7 +10,7 @@ notifications, } from "@budibase/bbui" import { goto, params } from "@roxi/routify" - import { auth, organisation, oidc, admin } from "stores/portal" + import { auth, organisation, oidc } from "stores/portal" import GoogleButton from "./_components/GoogleButton.svelte" import OIDCButton from "./_components/OIDCButton.svelte" import Logo from "assets/bb-emblem.svg" @@ -18,10 +18,8 @@ let username = "" let password = "" - let loaded = false $: company = $organisation.company || "Budibase" - $: multiTenancyEnabled = $admin.multiTenancy async function login() { try { @@ -29,6 +27,7 @@ username, password, }) + notifications.success("Logged in successfully") if ($auth?.user?.forceResetPassword) { $goto("./reset") } else { @@ -51,7 +50,6 @@ onMount(async () => { await organisation.init() - loaded = true }) @@ -63,10 +61,8 @@ logo Sign in to {company} - {#if loaded} - - - {/if} + + Sign in with email @@ -83,17 +79,6 @@ $goto("./forgot")}> Forgot password? - {#if multiTenancyEnabled} - { - admin.unload() - $goto("./org") - }} - > - Change organisation - - {/if} diff --git a/packages/builder/src/pages/builder/auth/org.svelte b/packages/builder/src/pages/builder/auth/org.svelte deleted file mode 100644 index 785cf05914..0000000000 --- a/packages/builder/src/pages/builder/auth/org.svelte +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - diff --git a/packages/builder/src/pages/builder/index.svelte b/packages/builder/src/pages/builder/index.svelte index fba581a046..a4fe10f10d 100644 --- a/packages/builder/src/pages/builder/index.svelte +++ b/packages/builder/src/pages/builder/index.svelte @@ -2,15 +2,13 @@ import { redirect } from "@roxi/routify" import { auth } from "stores/portal" - auth.checkQueryString() - $: { if (!$auth.user) { - $redirect(`./auth`) + $redirect("./auth/login") } else if ($auth.user.builder?.global) { - $redirect(`./portal`) + $redirect("./portal") } else { - $redirect(`./apps`) + $redirect("./apps") } } diff --git a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte index 458097bdb0..13a5f3e04c 100644 --- a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte @@ -7,6 +7,7 @@ import OneLoginLogo from "assets/onelogin-logo.png" import OidcLogoPng from "assets/oidc-logo.png" import { isEqual, cloneDeep } from "lodash/fp" + import { Button, Heading, @@ -21,51 +22,36 @@ } from "@budibase/bbui" import { onMount } from "svelte" import api from "builderStore/api" - import { organisation, auth, admin } from "stores/portal" + import { organisation } from "stores/portal" import { uuid } from "builderStore/uuid" - $: tenantId = $auth.tenantId - $: multiTenancyEnabled = $admin.multiTenancy - const ConfigTypes = { Google: "google", OIDC: "oidc", + // Github: "github", + // AzureAD: "ad", } - function callbackUrl(tenantId, end) { - let url = `/api/global/auth` - if (multiTenancyEnabled && tenantId) { - url += `/${tenantId}` - } - url += end - return url + const GoogleConfigFields = { + Google: ["clientID", "clientSecret", "callbackURL"], + } + const GoogleConfigLabels = { + Google: { + clientID: "Client ID", + clientSecret: "Client secret", + callbackURL: "Callback URL", + }, } - $: GoogleConfigFields = { - Google: [ - { name: "clientID", label: "Client ID" }, - { name: "clientSecret", label: "Client secret" }, - { - name: "callbackURL", - label: "Callback URL", - readonly: true, - placeholder: callbackUrl(tenantId, "/google/callback"), - }, - ], + const OIDCConfigFields = { + Oidc: ["configUrl", "clientID", "clientSecret"], } - - $: OIDCConfigFields = { - Oidc: [ - { name: "configUrl", label: "Config URL" }, - { name: "clientID", label: "Client ID" }, - { name: "clientSecret", label: "Client Secret" }, - { - name: "callbackURL", - label: "Callback URL", - readonly: true, - placeholder: callbackUrl(tenantId, "/oidc/callback"), - }, - ], + const OIDCConfigLabels = { + Oidc: { + configUrl: "Config URL", + clientID: "Client ID", + clientSecret: "Client Secret", + }, } let iconDropdownOptions = [ @@ -123,13 +109,17 @@ // Create a flag so that it will only try to save completed forms $: partialGoogle = - providers.google?.config?.clientID || providers.google?.config?.clientSecret + providers.google?.config?.clientID || + providers.google?.config?.clientSecret || + providers.google?.config?.callbackURL $: partialOidc = providers.oidc?.config?.configs[0].configUrl || providers.oidc?.config?.configs[0].clientID || providers.oidc?.config?.configs[0].clientSecret $: googleComplete = - providers.google?.config?.clientID && providers.google?.config?.clientSecret + providers.google?.config?.clientID && + providers.google?.config?.clientSecret && + providers.google?.config?.callbackURL $: oidcComplete = providers.oidc?.config?.configs[0].configUrl && providers.oidc?.config?.configs[0].clientID && @@ -139,7 +129,7 @@ let data = new FormData() data.append("file", file) const res = await api.post( - `/api/global/configs/upload/logos_oidc/${file.name}`, + `/api/admin/configs/upload/logos_oidc/${file.name}`, data, {} ) @@ -159,21 +149,17 @@ let calls = [] docs.forEach(element => { if (element.type === ConfigTypes.OIDC) { - //Add a UUID here so each config is distinguishable when it arrives at the login page - for (let config of element.config.configs) { - if (!config.uuid) { - config.uuid = uuid() - } - // callback urls shouldn't be included - delete config.callbackURL - } + //Add a UUID here so each config is distinguishable when it arrives at the login page. + element.config.configs.forEach(config => { + !config.uuid && (config.uuid = uuid()) + }) if (partialOidc) { if (!oidcComplete) { notifications.error( `Please fill in all required ${ConfigTypes.OIDC} fields` ) } else { - calls.push(api.post(`/api/global/configs`, element)) + calls.push(api.post(`/api/admin/configs`, element)) // turn the save button grey when clicked oidcSaveButtonDisabled = true originalOidcDoc = cloneDeep(providers.oidc) @@ -187,8 +173,7 @@ `Please fill in all required ${ConfigTypes.Google} fields` ) } else { - delete element.config.callbackURL - calls.push(api.post(`/api/global/configs`, element)) + calls.push(api.post(`/api/admin/configs`, element)) googleSaveButtonDisabled = true originalGoogleDoc = cloneDeep(providers.google) } @@ -221,7 +206,7 @@ await organisation.init() // fetch the configs for oauth const googleResponse = await api.get( - `/api/global/configs/${ConfigTypes.Google}` + `/api/admin/configs/${ConfigTypes.Google}` ) const googleDoc = await googleResponse.json() @@ -242,7 +227,7 @@ //Get the list of user uploaded logos and push it to the dropdown options. //This needs to be done before the config call so they're available when the dropdown renders - const res = await api.get(`/api/global/configs/logos_oidc`) + const res = await api.get(`/api/admin/configs/logos_oidc`) const configSettings = await res.json() if (configSettings.config) { @@ -257,16 +242,17 @@ }) }) } - const oidcResponse = await api.get( - `/api/global/configs/${ConfigTypes.OIDC}` - ) + const oidcResponse = await api.get(`/api/admin/configs/${ConfigTypes.OIDC}`) const oidcDoc = await oidcResponse.json() if (!oidcDoc._id) { + console.log("hi") + providers.oidc = { type: ConfigTypes.OIDC, config: { configs: [{ activated: true }] }, } } else { + console.log("hello") originalOidcDoc = cloneDeep(oidcDoc) providers.oidc = oidcDoc } @@ -309,12 +295,8 @@ {#each GoogleConfigFields.Google as field}
- - + +
{/each}
@@ -353,14 +335,14 @@ {#each OIDCConfigFields.Oidc as field}
- - + +
{/each} +
+ + +

To customize your login button, fill out the fields below. diff --git a/packages/builder/src/pages/builder/portal/manage/email/index.svelte b/packages/builder/src/pages/builder/portal/manage/email/index.svelte index f94d2bcd0f..4a2b21b691 100644 --- a/packages/builder/src/pages/builder/portal/manage/email/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/email/index.svelte @@ -53,7 +53,7 @@ delete smtp.config.auth } // Save your SMTP config - const response = await api.post(`/api/global/configs`, smtp) + const response = await api.post(`/api/admin/configs`, smtp) if (response.status !== 200) { const error = await response.text() @@ -75,9 +75,7 @@ async function fetchSmtp() { loading = true // fetch the configs for smtp - const smtpResponse = await api.get( - `/api/global/configs/${ConfigTypes.SMTP}` - ) + const smtpResponse = await api.get(`/api/admin/configs/${ConfigTypes.SMTP}`) const smtpDoc = await smtpResponse.json() if (!smtpDoc._id) { @@ -94,13 +92,8 @@ requireAuth = smtpConfig.config.auth != null // always attach the auth for the forms purpose - // this will be removed later if required - if (!smtpDoc.config) { - smtpDoc.config = {} - } - if (!smtpDoc.config.auth) { - smtpConfig.config.auth = { - type: "login", - } + smtpConfig.config.auth = { + type: "login", } } diff --git a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte index c0f21bb554..ebb01bd336 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte @@ -47,8 +47,8 @@ }) let selectedApp - const userFetch = fetchData(`/api/global/users/${userId}`) - const apps = fetchData(`/api/global/roles`) + const userFetch = fetchData(`/api/admin/users/${userId}`) + const apps = fetchData(`/api/admin/roles`) async function deleteUser() { const res = await users.delete(userId) diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte index b274d3af91..682b0c4ee9 100644 --- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte +++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte @@ -37,7 +37,7 @@ async function uploadLogo(file) { let data = new FormData() data.append("file", file) - const res = await post("/api/global/configs/upload/settings/logo", data, {}) + const res = await post("/api/admin/configs/upload/settings/logo", data, {}) return await res.json() } diff --git a/packages/builder/src/pages/index.svelte b/packages/builder/src/pages/index.svelte index 477097f726..4c97b49763 100644 --- a/packages/builder/src/pages/index.svelte +++ b/packages/builder/src/pages/index.svelte @@ -1,11 +1,4 @@ diff --git a/packages/builder/src/stores/portal/admin.js b/packages/builder/src/stores/portal/admin.js index 0699daf8dc..33eb23a64d 100644 --- a/packages/builder/src/stores/portal/admin.js +++ b/packages/builder/src/stores/portal/admin.js @@ -1,18 +1,12 @@ -import { writable, get } from "svelte/store" +import { writable } from "svelte/store" import api from "builderStore/api" -import { auth } from "stores/portal" export function createAdminStore() { - const admin = writable({ - loaded: false, - }) + const { subscribe, set } = writable({}) async function init() { try { - const tenantId = get(auth).tenantId - const response = await api.get( - `/api/global/configs/checklist?tenantId=${tenantId}` - ) + const response = await api.get("/api/admin/configs/checklist") const json = await response.json() const onboardingSteps = Object.keys(json) @@ -22,49 +16,20 @@ export function createAdminStore() { 0 ) - await multiTenancyEnabled() - admin.update(store => { - store.loaded = true - store.checklist = json - store.onboardingProgress = - (stepsComplete / onboardingSteps.length) * 100 - return store + set({ + checklist: json, + onboardingProgress: (stepsComplete / onboardingSteps.length) * 100, }) } catch (err) { - admin.update(store => { - store.checklist = null - return store + set({ + checklist: null, }) } } - async function multiTenancyEnabled() { - let enabled = false - try { - const response = await api.get(`/api/system/flags`) - const json = await response.json() - enabled = json.multiTenancy - } catch (err) { - // just let it stay disabled - } - admin.update(store => { - store.multiTenancy = enabled - return store - }) - return enabled - } - - function unload() { - admin.update(store => { - store.loaded = false - return store - }) - } - return { - subscribe: admin.subscribe, + subscribe, init, - unload, } } diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index fe8f87cfb2..ef91c114f6 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -1,124 +1,74 @@ import { derived, writable, get } from "svelte/store" import api from "../../builderStore/api" -import { admin } from "stores/portal" export function createAuthStore() { - const auth = writable({ - user: null, - tenantId: "default", - tenantSet: false, - loaded: false, - }) - const store = derived(auth, $store => { + const user = writable(null) + const store = derived(user, $user => { let initials = null let isAdmin = false let isBuilder = false - if ($store.user) { - const user = $store.user - if (user.firstName) { - initials = user.firstName[0] - if (user.lastName) { - initials += user.lastName[0] + if ($user) { + if ($user.firstName) { + initials = $user.firstName[0] + if ($user.lastName) { + initials += $user.lastName[0] } - } else if (user.email) { - initials = user.email[0] + } else if ($user.email) { + initials = $user.email[0] } else { initials = "Unknown" } - isAdmin = !!user.admin?.global - isBuilder = !!user.builder?.global + isAdmin = !!$user.admin?.global + isBuilder = !!$user.builder?.global } return { - user: $store.user, - tenantId: $store.tenantId, - tenantSet: $store.tenantSet, - loaded: $store.loaded, + user: $user, initials, isAdmin, isBuilder, } }) - function setUser(user) { - auth.update(store => { - store.loaded = true - store.user = user - if (user) { - store.tenantId = user.tenantId || "default" - store.tenantSet = true - } - return store - }) - } - - async function setOrganisation(tenantId) { - const prevId = get(store).tenantId - auth.update(store => { - store.tenantId = tenantId - store.tenantSet = !!tenantId - return store - }) - if (prevId !== tenantId) { - // re-init admin after setting org - await admin.init() - } - } - return { subscribe: store.subscribe, - checkQueryString: async () => { - const urlParams = new URLSearchParams(window.location.search) - if (urlParams.has("tenantId")) { - const tenantId = urlParams.get("tenantId") - await setOrganisation(tenantId) - } - }, - setOrg: async tenantId => { - await setOrganisation(tenantId) - }, checkAuth: async () => { - const response = await api.get("/api/global/users/self") + const response = await api.get("/api/admin/users/self") if (response.status !== 200) { - setUser(null) + user.set(null) } else { const json = await response.json() - setUser(json) + user.set(json) } }, login: async creds => { - const tenantId = get(store).tenantId - const response = await api.post( - `/api/global/auth/${tenantId}/login`, - creds - ) + const response = await api.post(`/api/admin/auth`, creds) const json = await response.json() if (response.status === 200) { - setUser(json.user) + user.set(json.user) } else { throw "Invalid credentials" } return json }, logout: async () => { - const response = await api.post(`/api/global/auth/logout`) + const response = await api.post(`/api/admin/auth/logout`) if (response.status !== 200) { throw "Unable to create logout" } await response.json() - setUser(null) + user.set(null) }, updateSelf: async fields => { - const newUser = { ...get(auth).user, ...fields } - const response = await api.post("/api/global/users/self", newUser) + const newUser = { ...get(user), ...fields } + const response = await api.post("/api/admin/users/self", newUser) if (response.status === 200) { - setUser(newUser) + user.set(newUser) } else { throw "Unable to update user details" } }, forgotPassword: async email => { - const tenantId = get(store).tenantId - const response = await api.post(`/api/global/auth/${tenantId}/reset`, { + const response = await api.post(`/api/admin/auth/reset`, { email, }) if (response.status !== 200) { @@ -127,21 +77,17 @@ export function createAuthStore() { await response.json() }, resetPassword: async (password, code) => { - const tenantId = get(store).tenantId - const response = await api.post( - `/api/global/auth/${tenantId}/reset/update`, - { - password, - resetCode: code, - } - ) + const response = await api.post(`/api/admin/auth/reset/update`, { + password, + resetCode: code, + }) if (response.status !== 200) { throw "Unable to reset password" } await response.json() }, createUser: async user => { - const response = await api.post(`/api/global/users`, user) + const response = await api.post(`/api/admin/users`, user) if (response.status !== 200) { throw "Unable to create user" } diff --git a/packages/builder/src/stores/portal/email.js b/packages/builder/src/stores/portal/email.js index a015480141..4ec6d72d3e 100644 --- a/packages/builder/src/stores/portal/email.js +++ b/packages/builder/src/stores/portal/email.js @@ -9,11 +9,11 @@ export function createEmailStore() { templates: { fetch: async () => { // fetch the email template definitions - const response = await api.get(`/api/global/template/definitions`) + const response = await api.get(`/api/admin/template/definitions`) const definitions = await response.json() // fetch the email templates themselves - const templatesResponse = await api.get(`/api/global/template/email`) + const templatesResponse = await api.get(`/api/admin/template/email`) const templates = await templatesResponse.json() store.set({ @@ -23,7 +23,7 @@ export function createEmailStore() { }, save: async template => { // Save your template config - const response = await api.post(`/api/global/template`, template) + const response = await api.post(`/api/admin/template`, template) const json = await response.json() if (response.status !== 200) throw new Error(json.message) template._rev = json._rev diff --git a/packages/builder/src/stores/portal/oidc.js b/packages/builder/src/stores/portal/oidc.js index 3e3a7048ca..d8d06f12a9 100644 --- a/packages/builder/src/stores/portal/oidc.js +++ b/packages/builder/src/stores/portal/oidc.js @@ -1,6 +1,5 @@ -import { writable, get } from "svelte/store" +import { writable } from "svelte/store" import api from "builderStore/api" -import { auth } from "stores/portal" const OIDC_CONFIG = { logo: undefined, @@ -13,13 +12,10 @@ export function createOidcStore() { const { set, subscribe } = store async function init() { - const tenantId = get(auth).tenantId - const res = await api.get( - `/api/global/configs/public/oidc?tenantId=${tenantId}` - ) + const res = await api.get(`/api/admin/configs/publicOidc`) const json = await res.json() - if (json.status === 400 || Object.keys(json).length === 0) { + if (json.status === 400) { set(OIDC_CONFIG) } else { // Just use the first config for now. We will be support multiple logins buttons later on. diff --git a/packages/builder/src/stores/portal/organisation.js b/packages/builder/src/stores/portal/organisation.js index 03bfa6ca28..71c0be4b4d 100644 --- a/packages/builder/src/stores/portal/organisation.js +++ b/packages/builder/src/stores/portal/organisation.js @@ -1,9 +1,8 @@ import { writable, get } from "svelte/store" import api from "builderStore/api" -import { auth } from "stores/portal" const DEFAULT_CONFIG = { - platformUrl: "http://localhost:10000", + platformUrl: "http://localhost:1000", logoUrl: undefined, docsUrl: undefined, company: "Budibase", @@ -16,8 +15,7 @@ export function createOrganisationStore() { const { subscribe, set } = store async function init() { - const tenantId = get(auth).tenantId - const res = await api.get(`/api/global/configs/public?tenantId=${tenantId}`) + const res = await api.get(`/api/admin/configs/public`) const json = await res.json() if (json.status === 400) { @@ -28,7 +26,7 @@ export function createOrganisationStore() { } async function save(config) { - const res = await api.post("/api/global/configs", { + const res = await api.post("/api/admin/configs", { type: "settings", config: { ...get(store), ...config }, _rev: get(store)._rev, diff --git a/packages/builder/src/stores/portal/users.js b/packages/builder/src/stores/portal/users.js index 17299dc056..8a19f79809 100644 --- a/packages/builder/src/stores/portal/users.js +++ b/packages/builder/src/stores/portal/users.js @@ -6,7 +6,7 @@ export function createUsersStore() { const { subscribe, set } = writable([]) async function init() { - const response = await api.get(`/api/global/users`) + const response = await api.get(`/api/admin/users`) const json = await response.json() set(json) } @@ -23,12 +23,12 @@ export function createUsersStore() { global: true, } } - const response = await api.post(`/api/global/users/invite`, body) + const response = await api.post(`/api/admin/users/invite`, body) return await response.json() } async function acceptInvite(inviteCode, password) { - const response = await api.post("/api/global/users/invite/accept", { + const response = await api.post("/api/admin/users/invite/accept", { inviteCode, password, }) @@ -47,20 +47,20 @@ export function createUsersStore() { if (admin) { body.admin = { global: true } } - const response = await api.post("/api/global/users", body) + const response = await api.post("/api/admin/users", body) await init() return await response.json() } async function del(id) { - const response = await api.delete(`/api/global/users/${id}`) + const response = await api.delete(`/api/admin/users/${id}`) update(users => users.filter(user => user._id !== id)) return await response.json() } async function save(data) { try { - const res = await post(`/api/global/users`, data) + const res = await post(`/api/admin/users`, data) return await res.json() } catch (error) { console.log(error) diff --git a/packages/client/src/api/auth.js b/packages/client/src/api/auth.js index 68ca5dbc80..6ea105d9f9 100644 --- a/packages/client/src/api/auth.js +++ b/packages/client/src/api/auth.js @@ -13,7 +13,7 @@ export const logIn = async ({ email, password }) => { return API.error("Please enter your password") } return await API.post({ - url: "/api/global/auth", + url: "/api/admin/auth", body: { username: email, password }, }) } @@ -23,7 +23,7 @@ export const logIn = async ({ email, password }) => { */ export const fetchSelf = async () => { const user = await API.get({ url: "/api/self" }) - if (user && user._id) { + if (user?._id) { if (user.roleId === "PUBLIC") { // Don't try to enrich a public user as it will 403 return user diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/node-fetch.ts index dfb839fe85..eaac412854 100644 --- a/packages/server/__mocks__/node-fetch.ts +++ b/packages/server/__mocks__/node-fetch.ts @@ -16,7 +16,7 @@ module FetchMock { } } - if (url.includes("/api/global")) { + if (url.includes("/api/admin")) { return json({ email: "test@test.com", _id: "us_test@test.com", diff --git a/packages/server/package.json b/packages/server/package.json index fbdb2f55f2..b80f24ac33 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -23,9 +23,7 @@ "format": "prettier --config ../../.prettierrc.json 'src/**/*.ts' --write", "lint": "eslint --fix src/", "lint:fix": "yarn run format && yarn run lint", - "initialise": "node scripts/initialise.js", - "multi:enable": "node scripts/multiTenancy.js enable", - "multi:disable": "node scripts/multiTenancy.js disable" + "initialise": "node scripts/initialise.js" }, "jest": { "preset": "ts-jest", @@ -138,8 +136,7 @@ "supertest": "^4.0.2", "ts-jest": "^27.0.3", "ts-node": "^10.0.0", - "typescript": "^4.3.4", - "update-dotenv": "^1.1.1" + "typescript": "^4.3.4" }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" } diff --git a/packages/server/scripts/dev/manage.js b/packages/server/scripts/dev/manage.js index e0801bc9df..ffd8c6b9e3 100644 --- a/packages/server/scripts/dev/manage.js +++ b/packages/server/scripts/dev/manage.js @@ -33,29 +33,26 @@ async function init() { fs.writeFileSync(envoyOutputPath, processStringSync(contents, config)) const envFilePath = path.join(process.cwd(), ".env") - if (!fs.existsSync(envFilePath)) { - const envFileJson = { - PORT: 4001, - MINIO_URL: "http://localhost:10000/", - COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", - REDIS_URL: "localhost:6379", - WORKER_URL: "http://localhost:4002", - INTERNAL_API_KEY: "budibase", - JWT_SECRET: "testsecret", - REDIS_PASSWORD: "budibase", - MINIO_ACCESS_KEY: "budibase", - MINIO_SECRET_KEY: "budibase", - COUCH_DB_PASSWORD: "budibase", - COUCH_DB_USER: "budibase", - SELF_HOSTED: 1, - MULTI_TENANCY: "", - } - let envFile = "" - Object.keys(envFileJson).forEach(key => { - envFile += `${key}=${envFileJson[key]}\n` - }) - fs.writeFileSync(envFilePath, envFile) + const envFileJson = { + PORT: 4001, + MINIO_URL: "http://localhost:10000/", + COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", + REDIS_URL: "localhost:6379", + WORKER_URL: "http://localhost:4002", + INTERNAL_API_KEY: "budibase", + JWT_SECRET: "testsecret", + REDIS_PASSWORD: "budibase", + MINIO_ACCESS_KEY: "budibase", + MINIO_SECRET_KEY: "budibase", + COUCH_DB_PASSWORD: "budibase", + COUCH_DB_USER: "budibase", + SELF_HOSTED: 1, } + let envFile = "" + Object.keys(envFileJson).forEach(key => { + envFile += `${key}=${envFileJson[key]}\n` + }) + fs.writeFileSync(envFilePath, envFile) } async function up() { diff --git a/packages/server/scripts/integrations/postgres/init.sql b/packages/server/scripts/integrations/postgres/init.sql index cc2fc734f8..5a99520c1e 100644 --- a/packages/server/scripts/integrations/postgres/init.sql +++ b/packages/server/scripts/integrations/postgres/init.sql @@ -10,11 +10,10 @@ CREATE TABLE Persons ( CREATE TABLE Tasks ( TaskID SERIAL PRIMARY KEY, PersonID INT, - Completed BOOLEAN, TaskName varchar(255), CONSTRAINT fkPersons FOREIGN KEY(PersonID) - REFERENCES Persons(PersonID) + REFERENCES Persons(PersonID) ); CREATE TABLE Products ( ProductID SERIAL PRIMARY KEY, @@ -25,15 +24,15 @@ CREATE TABLE Products_Tasks ( TaskID INT NOT NULL, CONSTRAINT fkProducts FOREIGN KEY(ProductID) - REFERENCES Products(ProductID), + REFERENCES Products(ProductID), CONSTRAINT fkTasks FOREIGN KEY(TaskID) - REFERENCES Tasks(TaskID), + REFERENCES Tasks(TaskID), PRIMARY KEY (ProductID, TaskID) ); INSERT INTO Persons (FirstName, LastName, Address, City) VALUES ('Mike', 'Hughes', '123 Fake Street', 'Belfast'); -INSERT INTO Tasks (PersonID, TaskName, Completed) VALUES (1, 'assembling', TRUE); -INSERT INTO Tasks (PersonID, TaskName, Completed) VALUES (1, 'processing', FALSE); +INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'assembling'); +INSERT INTO Tasks (PersonID, TaskName) VALUES (1, 'processing'); INSERT INTO Products (ProductName) VALUES ('Computers'); INSERT INTO Products (ProductName) VALUES ('Laptops'); INSERT INTO Products (ProductName) VALUES ('Chairs'); diff --git a/packages/server/scripts/multiTenancy.js b/packages/server/scripts/multiTenancy.js deleted file mode 100644 index 89ee7398f3..0000000000 --- a/packages/server/scripts/multiTenancy.js +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env node -const updateDotEnv = require("update-dotenv") - -const arg = process.argv.slice(2)[0] - -updateDotEnv({ - MULTI_TENANCY: arg === "enable" ? "1" : "", -}).then(() => console.log("Updated server!")) diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js index 9b1ddee4c4..55422ee603 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.js @@ -1,30 +1,8 @@ -const { StaticDatabases } = require("@budibase/auth/db") -const { getGlobalDB } = require("@budibase/auth/tenancy") - -const KEYS_DOC = StaticDatabases.GLOBAL.docs.apiKeys - -async function getBuilderMainDoc() { - const db = getGlobalDB() - try { - return await db.get(KEYS_DOC) - } catch (err) { - // doesn't exist yet, nothing to get - return { - _id: KEYS_DOC, - } - } -} - -async function setBuilderMainDoc(doc) { - // make sure to override the ID - doc._id = KEYS_DOC - const db = getGlobalDB() - return db.put(doc) -} +const builderDB = require("../../db/builder") exports.fetch = async function (ctx) { try { - const mainDoc = await getBuilderMainDoc() + const mainDoc = await builderDB.getBuilderMainDoc() ctx.body = mainDoc.apiKeys ? mainDoc.apiKeys : {} } catch (err) { /* istanbul ignore next */ @@ -37,12 +15,12 @@ exports.update = async function (ctx) { const value = ctx.request.body.value try { - const mainDoc = await getBuilderMainDoc() + const mainDoc = await builderDB.getBuilderMainDoc() if (mainDoc.apiKeys == null) { mainDoc.apiKeys = {} } mainDoc.apiKeys[key] = value - const resp = await setBuilderMainDoc(mainDoc) + const resp = await builderDB.setBuilderMainDoc(mainDoc) ctx.body = { _id: resp.id, _rev: resp.rev, diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index d0de611d74..a2e254461a 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -25,7 +25,7 @@ const { BASE_LAYOUTS } = require("../../constants/layouts") const { createHomeScreen } = require("../../constants/screens") const { cloneDeep } = require("lodash/fp") const { processObject } = require("@budibase/string-templates") -const { getAllApps } = require("@budibase/auth/db") +const { getAllApps } = require("../../utilities") const { USERS_TABLE_SCHEMA } = require("../../constants") const { getDeployedApps, @@ -38,7 +38,6 @@ const { backupClientLibrary, revertClientLibrary, } = require("../../utilities/fileSystem/clientLibrary") -const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy") const URL_REGEX_SLASH = /\/|\\/g @@ -94,8 +93,7 @@ async function getAppUrlIfNotInUse(ctx) { } async function createInstance(template) { - const tenantId = isMultiTenant() ? getTenantId() : null - const baseAppId = generateAppID(tenantId) + const baseAppId = generateAppID() const appId = generateDevAppID(baseAppId) const db = new CouchDB(appId) @@ -130,7 +128,7 @@ async function createInstance(template) { exports.fetch = async function (ctx) { const dev = ctx.query && ctx.query.status === AppStatus.DEV const all = ctx.query && ctx.query.status === AppStatus.ALL - const apps = await getAllApps(CouchDB, { dev, all }) + const apps = await getAllApps({ CouchDB, dev, all }) // get the locks for all the dev apps if (dev || all) { @@ -222,12 +220,10 @@ exports.create = async function (ctx) { url: url, template: ctx.request.body.template, instance: instance, - tenantId: getTenantId(), updatedAt: new Date().toISOString(), createdAt: new Date().toISOString(), } - const response = await db.put(newApplication, { force: true }) - newApplication._rev = response.rev + await db.put(newApplication, { force: true }) await createEmptyAppPackage(ctx, newApplication) /* istanbul ignore next */ @@ -299,7 +295,7 @@ exports.delete = async function (ctx) { await deleteApp(ctx.params.appId) } // make sure the app/role doesn't stick around after the app has been deleted - await removeAppFromUserRoles(ctx, ctx.params.appId) + await removeAppFromUserRoles(ctx.params.appId) ctx.status = 200 ctx.body = result diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index 5078218fc7..da863f5493 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -22,7 +22,7 @@ exports.fetchSelf = async ctx => { const userTable = await db.get(InternalTables.USER_METADATA) const metadata = await db.get(userId) // specifically needs to make sure is enriched - ctx.body = await outputProcessing(ctx, userTable, { + ctx.body = await outputProcessing(appId, userTable, { ...user, ...metadata, }) diff --git a/packages/server/src/api/controllers/deploy/index.js b/packages/server/src/api/controllers/deploy/index.js index 4608ca6342..f5db81e6e8 100644 --- a/packages/server/src/api/controllers/deploy/index.js +++ b/packages/server/src/api/controllers/deploy/index.js @@ -1,6 +1,6 @@ -const CouchDB = require("../../../db") +const PouchDB = require("../../../db") const Deployment = require("./Deployment") -const { Replication } = require("@budibase/auth/db") +const { Replication, StaticDatabases } = require("@budibase/auth/db") const { DocumentTypes } = require("../../../db/utils") // the max time we can wait for an invalidation to complete before considering it failed @@ -31,14 +31,13 @@ async function checkAllDeployments(deployments) { async function storeDeploymentHistory(deployment) { const appId = deployment.getAppId() const deploymentJSON = deployment.getJSON() - const db = new CouchDB(appId) + const db = new PouchDB(StaticDatabases.DEPLOYMENTS.name) let deploymentDoc try { - // theres only one deployment doc per app database - deploymentDoc = await db.get(DocumentTypes.DEPLOYMENTS) + deploymentDoc = await db.get(appId) } catch (err) { - deploymentDoc = { _id: DocumentTypes.DEPLOYMENTS, history: {} } + deploymentDoc = { _id: appId, history: {} } } const deploymentId = deploymentJSON._id @@ -68,7 +67,7 @@ async function deployApp(deployment) { }) await replication.replicate() - const db = new CouchDB(productionAppId) + const db = new PouchDB(productionAppId) const appDoc = await db.get(DocumentTypes.APP_METADATA) appDoc.appId = productionAppId appDoc.instance._id = productionAppId @@ -99,9 +98,8 @@ async function deployApp(deployment) { exports.fetchDeployments = async function (ctx) { try { - const appId = ctx.appId - const db = new CouchDB(appId) - const deploymentDoc = await db.get(DocumentTypes.DEPLOYMENTS) + const db = new PouchDB(StaticDatabases.DEPLOYMENTS.name) + const deploymentDoc = await db.get(ctx.appId) const { updated, deployments } = await checkAllDeployments( deploymentDoc, ctx.user @@ -117,9 +115,8 @@ exports.fetchDeployments = async function (ctx) { exports.deploymentProgress = async function (ctx) { try { - const appId = ctx.appId - const db = new CouchDB(appId) - const deploymentDoc = await db.get(DocumentTypes.DEPLOYMENTS) + const db = new PouchDB(StaticDatabases.DEPLOYMENTS.name) + const deploymentDoc = await db.get(ctx.appId) ctx.body = deploymentDoc[ctx.params.deploymentId] } catch (err) { ctx.throw( diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index d75c4032d7..6dcd5727fb 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -9,9 +9,8 @@ const { DocumentTypes } = require("../../db/utils") async function redirect(ctx, method) { const { devPath } = ctx.params - const queryString = ctx.originalUrl.split("?")[1] || "" const response = await fetch( - checkSlashesInUrl(`${env.WORKER_URL}/api/global/${devPath}?${queryString}`), + checkSlashesInUrl(`${env.WORKER_URL}/api/admin/${devPath}`), request( ctx, { diff --git a/packages/server/src/api/controllers/row/internal.js b/packages/server/src/api/controllers/row/internal.js index 28c88c1eac..25ebb5375b 100644 --- a/packages/server/src/api/controllers/row/internal.js +++ b/packages/server/src/api/controllers/row/internal.js @@ -161,7 +161,7 @@ exports.fetchView = async ctx => { schema: {}, } } - rows = await outputProcessing(ctx, table, response.rows) + rows = await outputProcessing(appId, table, response.rows) } if (calculation === CALCULATION_TYPES.STATS) { @@ -204,7 +204,7 @@ exports.fetch = async ctx => { ) rows = response.rows.map(row => row.doc) } - return outputProcessing(ctx, table, rows) + return outputProcessing(appId, table, rows) } exports.find = async ctx => { @@ -212,7 +212,7 @@ exports.find = async ctx => { const db = new CouchDB(appId) const table = await db.get(ctx.params.tableId) let row = await findRow(ctx, db, ctx.params.tableId, ctx.params.rowId) - row = await outputProcessing(ctx, table, row) + row = await outputProcessing(appId, table, row) return row } @@ -291,7 +291,7 @@ exports.search = async ctx => { // Enrich search results with relationships if (response.rows && response.rows.length) { const table = await db.get(tableId) - response.rows = await outputProcessing(ctx, table, response.rows) + response.rows = await outputProcessing(appId, table, response.rows) } return response @@ -328,7 +328,7 @@ exports.fetchEnrichedRow = async ctx => { }) // need to include the IDs in these rows for any links they may have let linkedRows = await outputProcessing( - ctx, + appId, table, response.rows.map(row => row.doc) ) diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 935ead38b6..6778f983c2 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -17,7 +17,7 @@ function removeGlobalProps(user) { exports.fetchMetadata = async function (ctx) { const database = new CouchDB(ctx.appId) - const global = await getGlobalUsers(ctx, ctx.appId) + const global = await getGlobalUsers(ctx.appId) const metadata = ( await database.allDocs( getUserMetadataParams(null, { diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index 81601eea1a..6c4188a5dc 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -1,6 +1,5 @@ const Router = require("@koa/router") -const { buildAuthMiddleware, auditLog, buildTenancyMiddleware } = - require("@budibase/auth").auth +const { buildAuthMiddleware, auditLog } = require("@budibase/auth").auth const currentApp = require("../middleware/currentapp") const compress = require("koa-compress") const zlib = require("zlib") @@ -10,13 +9,6 @@ const env = require("../environment") const router = new Router() -const NO_TENANCY_ENDPOINTS = [ - { - route: "/api/analytics", - method: "GET", - }, -] - router .use( compress({ @@ -44,8 +36,6 @@ router publicAllowed: true, }) ) - // nothing in the server should allow query string tenants - .use(buildTenancyMiddleware(null, NO_TENANCY_ENDPOINTS)) .use(currentApp) .use(auditLog) diff --git a/packages/server/src/api/routes/application.js b/packages/server/src/api/routes/application.js index c1d39acbd5..c2eb19e101 100644 --- a/packages/server/src/api/routes/application.js +++ b/packages/server/src/api/routes/application.js @@ -6,11 +6,11 @@ const { BUILDER } = require("@budibase/auth/permissions") const router = Router() router - .post("/api/applications", authorized(BUILDER), controller.create) .get("/api/applications/:appId/definition", controller.fetchAppDefinition) .get("/api/applications", controller.fetch) .get("/api/applications/:appId/appPackage", controller.fetchAppPackage) .put("/api/applications/:appId", authorized(BUILDER), controller.update) + .post("/api/applications", authorized(BUILDER), controller.create) .post( "/api/applications/:appId/client/update", authorized(BUILDER), diff --git a/packages/server/src/api/routes/dev.js b/packages/server/src/api/routes/dev.js index 7612d332dd..cd4c6e8fde 100644 --- a/packages/server/src/api/routes/dev.js +++ b/packages/server/src/api/routes/dev.js @@ -8,9 +8,9 @@ const router = Router() if (env.isDev() || env.isTest()) { router - .get("/api/global/:devPath(.*)", controller.redirectGet) - .post("/api/global/:devPath(.*)", controller.redirectPost) - .delete("/api/global/:devPath(.*)", controller.redirectDelete) + .get("/api/admin/:devPath(.*)", controller.redirectGet) + .post("/api/admin/:devPath(.*)", controller.redirectPost) + .delete("/api/admin/:devPath(.*)", controller.redirectDelete) } router diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index d089d7775d..9515384608 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -387,7 +387,7 @@ describe("/rows", () => { }) // the environment needs configured for this await setup.switchToSelfHosted(async () => { - const enriched = await outputProcessing({ appId: config.getAppId() }, table, [row]) + const enriched = await outputProcessing(config.getAppId(), table, [row]) expect(enriched[0].attachment[0].url).toBe( `/prod-budi-app-assets/${config.getAppId()}/attachments/test/thing.csv` ) diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js index 944d2ac527..3ba5b4d694 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.js +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -3,7 +3,6 @@ const appController = require("../../../controllers/application") const CouchDB = require("../../../../db") const { AppStatus } = require("../../../../db/utils") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") -const { TENANT_ID } = require("../../../../tests/utilities/structures") function Request(appId, params) { this.appId = appId @@ -17,8 +16,8 @@ exports.getAllTableRows = async config => { return req.body } -exports.clearAllApps = async (tenantId = TENANT_ID) => { - const req = { query: { status: AppStatus.DEV }, user: { tenantId } } +exports.clearAllApps = async () => { + const req = { query: { status: AppStatus.DEV } } await appController.fetch(req) const apps = req.body if (!apps || apps.length <= 0) { diff --git a/packages/server/src/automations/steps/serverLog.js b/packages/server/src/automations/steps/serverLog.js index 7389b65f54..c28309c5c8 100644 --- a/packages/server/src/automations/steps/serverLog.js +++ b/packages/server/src/automations/steps/serverLog.js @@ -19,7 +19,7 @@ module.exports.definition = { properties: { text: { type: "string", - title: "Log", + title: "URL", }, }, required: ["text"], diff --git a/packages/server/src/automations/thread.js b/packages/server/src/automations/thread.js index aada0ca0ca..7b6d969a98 100644 --- a/packages/server/src/automations/thread.js +++ b/packages/server/src/automations/thread.js @@ -3,10 +3,6 @@ const logic = require("./logic") const automationUtils = require("./automationUtils") const AutomationEmitter = require("../events/AutomationEmitter") const { processObject } = require("@budibase/string-templates") -const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants -const CouchDB = require("../db") -const { DocumentTypes } = require("../db/utils") -const { doInTenant } = require("@budibase/auth/tenancy") const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId @@ -20,7 +16,6 @@ class Orchestrator { this._metadata = triggerOutput.metadata this._chainCount = this._metadata ? this._metadata.automationChainCount : 0 this._appId = triggerOutput.appId - this._app = null // remove from context delete triggerOutput.appId delete triggerOutput.metadata @@ -45,19 +40,8 @@ class Orchestrator { return step } - async getApp() { - const appId = this._appId - if (this._app) { - return this._app - } - const db = new CouchDB(appId) - this._app = await db.get(DocumentTypes.APP_METADATA) - return this._app - } - async execute() { let automation = this._automation - const app = await this.getApp() for (let step of automation.definition.steps) { let stepFn = await this.getStepFunctionality(step.type, step.stepId) step.inputs = await processObject(step.inputs, this._context) @@ -67,15 +51,12 @@ class Orchestrator { ) // appId is always passed try { - let tenantId = app.tenantId || DEFAULT_TENANT_ID - const outputs = await doInTenant(tenantId, () => { - return stepFn({ - inputs: step.inputs, - appId: this._appId, - apiKey: automation.apiKey, - emitter: this._emitter, - context: this._context, - }) + const outputs = await stepFn({ + inputs: step.inputs, + appId: this._appId, + apiKey: automation.apiKey, + emitter: this._emitter, + context: this._context, }) if (step.stepId === FILTER_STEP_ID && !outputs.success) { break diff --git a/packages/server/src/db/builder.js b/packages/server/src/db/builder.js new file mode 100644 index 0000000000..d2bbcd404b --- /dev/null +++ b/packages/server/src/db/builder.js @@ -0,0 +1,38 @@ +const CouchDB = require("./index") +const { StaticDatabases } = require("./utils") +const env = require("../environment") + +const SELF_HOST_ERR = "Unable to access builder DB/doc - not self hosted." +const BUILDER_DB = StaticDatabases.BUILDER + +/** + * This is the builder database, right now this is a single, static database + * that is present across the whole system and determines some core functionality + * for the builder (e.g. storage of API keys). This has been limited to self hosting + * as it doesn't make as much sense against the currently design Cloud system. + */ + +exports.getBuilderMainDoc = async () => { + if (!env.SELF_HOSTED) { + throw SELF_HOST_ERR + } + const db = new CouchDB(BUILDER_DB.name) + try { + return await db.get(BUILDER_DB.baseDoc) + } catch (err) { + // doesn't exist yet, nothing to get + return { + _id: BUILDER_DB.baseDoc, + } + } +} + +exports.setBuilderMainDoc = async doc => { + if (!env.SELF_HOSTED) { + throw SELF_HOST_ERR + } + // make sure to override the ID + doc._id = BUILDER_DB.baseDoc + const db = new CouchDB(BUILDER_DB.name) + return db.put(doc) +} diff --git a/packages/server/src/db/linkedRows/index.js b/packages/server/src/db/linkedRows/index.js index dece78dcff..754340046d 100644 --- a/packages/server/src/db/linkedRows/index.js +++ b/packages/server/src/db/linkedRows/index.js @@ -60,7 +60,7 @@ async function getLinksForRows(appId, rows) { ) } -async function getFullLinkedDocs(ctx, appId, links) { +async function getFullLinkedDocs(appId, links) { // create DBs const db = new CouchDB(appId) const linkedRowIds = links.map(link => link.id) @@ -71,7 +71,7 @@ async function getFullLinkedDocs(ctx, appId, links) { let [users, other] = partition(linked, linkRow => linkRow._id.startsWith(USER_METDATA_PREFIX) ) - const globalUsers = await getGlobalUsers(ctx, appId, users) + const globalUsers = await getGlobalUsers(appId, users) users = users.map(user => { const globalUser = globalUsers.find( globalUser => globalUser && user._id.includes(globalUser._id) @@ -166,13 +166,12 @@ exports.attachLinkIDs = async (appId, rows) => { /** * Given a table and a list of rows this will retrieve all of the attached docs and enrich them into the row. * This is required for formula fields, this may only be utilised internally (for now). - * @param {object} ctx The request which is looking for rows. + * @param {string} appId The app in which the tables/rows/links exist. * @param {object} table The table from which the rows originated. * @param {array} rows The rows which are to be enriched. * @return {Promise<*>} returns the rows with all of the enriched relationships on it. */ -exports.attachFullLinkedDocs = async (ctx, table, rows) => { - const appId = ctx.appId +exports.attachFullLinkedDocs = async (appId, table, rows) => { const linkedTableIds = getLinkedTableIDs(table) if (linkedTableIds.length === 0) { return rows @@ -183,7 +182,7 @@ exports.attachFullLinkedDocs = async (ctx, table, rows) => { const links = (await getLinksForRows(appId, rows)).filter(link => rows.some(row => row._id === link.thisId) ) - let linked = await getFullLinkedDocs(ctx, appId, links) + let linked = await getFullLinkedDocs(appId, links) const linkedTables = [] for (let row of rows) { for (let link of links.filter(link => link.thisId === row._id)) { diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 92734c5e7b..74ddf87176 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -34,7 +34,6 @@ const DocumentTypes = { DATASOURCE: "datasource", DATASOURCE_PLUS: "datasource_plus", QUERY: "query", - DEPLOYMENTS: "deployments", } const ViewNames = { @@ -50,7 +49,13 @@ const SearchIndexes = { ROWS: "rows", } -exports.StaticDatabases = StaticDatabases +exports.StaticDatabases = { + BUILDER: { + name: "builder-db", + baseDoc: "builder-doc", + }, + ...StaticDatabases, +} const BudibaseInternalDB = { _id: "bb_internal", @@ -225,12 +230,8 @@ exports.getLinkParams = (otherProps = {}) => { * Generates a new app ID. * @returns {string} The new app ID which the app doc can be stored under. */ -exports.generateAppID = (tenantId = null) => { - let id = `${DocumentTypes.APP}${SEPARATOR}` - if (tenantId) { - id += `${tenantId}${SEPARATOR}` - } - return `${id}${newid()}` +exports.generateAppID = () => { + return `${DocumentTypes.APP}${SEPARATOR}${newid()}` } /** @@ -239,8 +240,8 @@ exports.generateAppID = (tenantId = null) => { */ exports.generateDevAppID = appId => { const prefix = `${DocumentTypes.APP}${SEPARATOR}` - const rest = appId.split(prefix)[1] - return `${DocumentTypes.APP_DEV}${SEPARATOR}${rest}` + const uuid = appId.split(prefix)[1] + return `${DocumentTypes.APP_DEV}${SEPARATOR}${uuid}` } /** diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js index 9f69664ffb..52c680f65a 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.js @@ -35,7 +35,6 @@ module.exports = { REDIS_URL: process.env.REDIS_URL, REDIS_PASSWORD: process.env.REDIS_PASSWORD, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, - MULTI_TENANCY: process.env.MULTI_TENANCY, // environment NODE_ENV: process.env.NODE_ENV, JEST_WORKER_ID: process.env.JEST_WORKER_ID, diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index 8f2403cc37..7169a36320 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -68,6 +68,5 @@ module.exports = async (ctx, next) => { ) { setCookie(ctx, { appId }, Cookies.CurrentApp) } - return next() } diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 0eb3851d98..4b9fe73424 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -10,19 +10,16 @@ const { basicScreen, basicLayout, basicWebhook, - TENANT_ID, } = require("./structures") const controllers = require("./controllers") const supertest = require("supertest") const { cleanup } = require("../../utilities/fileSystem") const { Cookies, Headers } = require("@budibase/auth").constants const { jwt } = require("@budibase/auth").auth -const auth = require("@budibase/auth") -const { getGlobalDB } = require("@budibase/auth/tenancy") +const { StaticDatabases } = require("@budibase/auth/db") const { createASession } = require("@budibase/auth/sessions") const { user: userCache } = require("@budibase/auth/cache") const CouchDB = require("../../db") -auth.init(CouchDB) const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -55,7 +52,7 @@ class TestConfiguration { request.cookies = { set: () => {}, get: () => {} } request.config = { jwtSecret: env.JWT_SECRET } request.appId = this.appId - request.user = { appId: this.appId, tenantId: TENANT_ID } + request.user = { appId: this.appId } request.query = {} request.request = { body: config, @@ -68,7 +65,7 @@ class TestConfiguration { } async globalUser(id = GLOBAL_USER_ID, builder = true, roles) { - const db = getGlobalDB(TENANT_ID) + const db = new CouchDB(StaticDatabases.GLOBAL.name) let existing try { existing = await db.get(id) @@ -79,9 +76,8 @@ class TestConfiguration { _id: id, ...existing, roles: roles || {}, - tenantId: TENANT_ID, } - await createASession(id, { sessionId: "sessionid", tenantId: TENANT_ID }) + await createASession(id, "sessionid") if (builder) { user.builder = { global: true } } @@ -111,7 +107,6 @@ class TestConfiguration { const auth = { userId: GLOBAL_USER_ID, sessionId: "sessionid", - tenantId: TENANT_ID, } const app = { roleId: BUILTIN_ROLE_IDS.ADMIN, @@ -338,15 +333,11 @@ class TestConfiguration { if (!email || !password) { await this.createUser() } - await createASession(userId, { - sessionId: "sessionid", - tenantId: TENANT_ID, - }) + await createASession(userId, "sessionid") // have to fake this const auth = { userId, sessionId: "sessionid", - tenantId: TENANT_ID, } const app = { roleId: roleId, diff --git a/packages/server/src/tests/utilities/structures.js b/packages/server/src/tests/utilities/structures.js index e4b2c7e1f0..91996a7804 100644 --- a/packages/server/src/tests/utilities/structures.js +++ b/packages/server/src/tests/utilities/structures.js @@ -4,8 +4,6 @@ const { createHomeScreen } = require("../../constants/screens") const { EMPTY_LAYOUT } = require("../../constants/layouts") const { cloneDeep } = require("lodash/fp") -exports.TENANT_ID = "default" - exports.basicTable = () => { return { name: "TestTable", diff --git a/packages/server/src/utilities/global.js b/packages/server/src/utilities/global.js index 2757398586..3ce794b406 100644 --- a/packages/server/src/utilities/global.js +++ b/packages/server/src/utilities/global.js @@ -1,12 +1,13 @@ +const CouchDB = require("../db") const { getMultiIDParams, getGlobalIDFromUserMetadataID, + StaticDatabases, } = require("../db/utils") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { getDeployedAppID } = require("@budibase/auth/db") const { getGlobalUserParams } = require("@budibase/auth/db") const { user: userCache } = require("@budibase/auth/cache") -const { getGlobalDB } = require("@budibase/auth/tenancy") exports.updateAppRole = (appId, user) => { if (!user.roles) { @@ -33,20 +34,18 @@ function processUser(appId, user) { } exports.getCachedSelf = async (ctx, appId) => { - // this has to be tenant aware, can't depend on the context to find it out - // running some middlewares before the tenancy causes context to break const user = await userCache.getUser(ctx.user._id) return processUser(appId, user) } -exports.getGlobalUser = async (ctx, appId, userId) => { - const db = getGlobalDB() +exports.getGlobalUser = async (appId, userId) => { + const db = CouchDB(StaticDatabases.GLOBAL.name) let user = await db.get(getGlobalIDFromUserMetadataID(userId)) return processUser(appId, user) } -exports.getGlobalUsers = async (ctx, appId = null, users = null) => { - const db = getGlobalDB() +exports.getGlobalUsers = async (appId = null, users = null) => { + const db = CouchDB(StaticDatabases.GLOBAL.name) let globalUsers if (users) { const globalIds = users.map(user => getGlobalIDFromUserMetadataID(user._id)) diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index 182ad51828..320d4a3eb5 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -1,5 +1,6 @@ const env = require("../environment") const { OBJ_STORE_DIRECTORY, ObjectStoreBuckets } = require("../constants") +const { getAllApps } = require("@budibase/auth/db") const { sanitizeKey } = require("@budibase/auth/src/objectStore") const BB_CDN = "https://cdn.app.budi.live/assets" @@ -7,6 +8,7 @@ const BB_CDN = "https://cdn.app.budi.live/assets" exports.wait = ms => new Promise(resolve => setTimeout(resolve, ms)) exports.isDev = env.isDev +exports.getAllApps = getAllApps /** * Makes sure that a URL has the correct number of slashes, while maintaining the diff --git a/packages/server/src/utilities/redis.js b/packages/server/src/utilities/redis.js index 6f1cf46606..e1fa632003 100644 --- a/packages/server/src/utilities/redis.js +++ b/packages/server/src/utilities/redis.js @@ -7,10 +7,8 @@ let devAppClient, debounceClient // we init this as we want to keep the connection open all the time // reduces the performance hit exports.init = async () => { - devAppClient = new Client(utils.Databases.DEV_LOCKS) - debounceClient = new Client(utils.Databases.DEBOUNCE) - await devAppClient.init() - await debounceClient.init() + devAppClient = await new Client(utils.Databases.DEV_LOCKS).init() + debounceClient = await new Client(utils.Databases.DEBOUNCE).init() } exports.shutdown = async () => { diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js index 618897383b..766bc09b2f 100644 --- a/packages/server/src/utilities/rowProcessor.js +++ b/packages/server/src/utilities/rowProcessor.js @@ -193,21 +193,20 @@ exports.inputProcessing = (user = {}, table, row) => { /** * This function enriches the input rows with anything they are supposed to contain, for example * link records or attachment links. - * @param {object} ctx the request which is looking for enriched rows. + * @param {string} appId the ID of the application for which rows are being enriched. * @param {object} table the table from which these rows came from originally, this is used to determine * the schema of the rows and then enrich. * @param {object[]} rows the rows which are to be enriched. * @returns {object[]} the enriched rows will be returned. */ -exports.outputProcessing = async (ctx, table, rows) => { - const appId = ctx.appId +exports.outputProcessing = async (appId, table, rows) => { let wasArray = true if (!(rows instanceof Array)) { rows = [rows] wasArray = false } // attach any linked row information - let enriched = await linkRows.attachFullLinkedDocs(ctx, table, rows) + let enriched = await linkRows.attachFullLinkedDocs(appId, table, rows) // process formulas enriched = processFormulas(table, enriched) diff --git a/packages/server/src/utilities/users.js b/packages/server/src/utilities/users.js index 64fbfb7ea2..6144397bf1 100644 --- a/packages/server/src/utilities/users.js +++ b/packages/server/src/utilities/users.js @@ -3,7 +3,7 @@ const { InternalTables } = require("../db/utils") const { getGlobalUser } = require("../utilities/global") exports.getFullUser = async (ctx, userId) => { - const global = await getGlobalUser(ctx, ctx.appId, userId) + const global = await getGlobalUser(ctx.appId, userId) let metadata try { // this will throw an error if the db doesn't exist, or there is no appId diff --git a/packages/server/src/utilities/workerRequests.js b/packages/server/src/utilities/workerRequests.js index 066f6e23d4..4a8d10ecb8 100644 --- a/packages/server/src/utilities/workerRequests.js +++ b/packages/server/src/utilities/workerRequests.js @@ -4,17 +4,13 @@ const { checkSlashesInUrl } = require("./index") const { getDeployedAppID } = require("@budibase/auth/db") const { updateAppRole, getGlobalUser } = require("./global") const { Headers } = require("@budibase/auth/constants") -const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy") -function request(ctx, request) { +function request(ctx, request, noApiKey) { if (!request.headers) { request.headers = {} } - if (!ctx) { + if (!noApiKey) { request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY - if (isTenantIdSet()) { - request.headers[Headers.TENANT_ID] = getTenantId() - } } if (request.body && Object.keys(request.body).length > 0) { request.headers["Content-Type"] = "application/json" @@ -33,11 +29,9 @@ function request(ctx, request) { exports.request = request -// have to pass in the tenant ID as this could be coming from an automation exports.sendSmtpEmail = async (to, from, subject, contents) => { - // tenant ID will be set in header const response = await fetch( - checkSlashesInUrl(env.WORKER_URL + `/api/global/email/send`), + checkSlashesInUrl(env.WORKER_URL + `/api/admin/email/send`), request(null, { method: "POST", body: { @@ -80,11 +74,11 @@ exports.getDeployedApps = async ctx => { } exports.getGlobalSelf = async (ctx, appId = null) => { - const endpoint = `/api/global/users/self` + const endpoint = `/api/admin/users/self` const response = await fetch( checkSlashesInUrl(env.WORKER_URL + endpoint), // we don't want to use API key when getting self - request(ctx, { method: "GET" }) + request(ctx, { method: "GET" }, true) ) if (response.status !== 200) { ctx.throw(400, "Unable to get self globally.") @@ -102,11 +96,11 @@ exports.addAppRoleToUser = async (ctx, appId, roleId, userId = null) => { body = {} if (!userId) { user = await exports.getGlobalSelf(ctx) - endpoint = `/api/global/users/self` + endpoint = `/api/admin/users/self` } else { - user = await getGlobalUser(ctx, appId, userId) + user = await getGlobalUser(appId, userId) body._id = userId - endpoint = `/api/global/users` + endpoint = `/api/admin/users` } body = { ...body, @@ -128,11 +122,11 @@ exports.addAppRoleToUser = async (ctx, appId, roleId, userId = null) => { return response.json() } -exports.removeAppFromUserRoles = async (ctx, appId) => { +exports.removeAppFromUserRoles = async appId => { const deployedAppId = getDeployedAppID(appId) const response = await fetch( - checkSlashesInUrl(env.WORKER_URL + `/api/global/roles/${deployedAppId}`), - request(ctx, { + checkSlashesInUrl(env.WORKER_URL + `/api/admin/roles/${deployedAppId}`), + request(null, { method: "DELETE", }) ) diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index c7f7bca0a1..c4be7d4512 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1146,11 +1146,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/auth@^0.9.79-alpha.4": - version "0.9.79" - resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.79.tgz#416271ffc55e84116550469656bf151a7734a90f" - integrity sha512-ENh099tYeUfVExsAeoxwMh2ODioKQGPteK9LJiU5hMdM4Oi7pyImu287BgKpTIheB+WtadT4e21VpPaJ62APEw== +"@budibase/auth@^0.9.80-alpha.7": + version "0.9.80-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.80-alpha.7.tgz#6fb4c40a5f437bb9f7e49c9acafbc601b0dffa49" + integrity sha512-9KZy8hqdpaWRY2n3pRAThP4Jb9TsrfJsJFdfDndJtPO1tTNKtDw2LGEwrT5Kym0a0SBHEzVrXq1Vw/sg72ACIQ== dependencies: + "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" bcryptjs "^2.4.3" ioredis "^4.27.1" @@ -1167,10 +1168,10 @@ uuid "^8.3.2" zlib "^1.0.5" -"@budibase/bbui@^0.9.79": - version "0.9.79" - resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.79.tgz#c033ba0af41cb584d2657a8353f9887328f6633f" - integrity sha512-XxUJSPGd2FZDFdbNOeMUXohhID5h3DVq9XyKTe6WhYax4m2da/2WTENJ16UFvmfA+yxLN1qSDeweq9vw2zCahQ== +"@budibase/bbui@^0.9.80-alpha.7": + version "0.9.80-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.80-alpha.7.tgz#5fbb7a6617a35a560151377fdc67a845f5620803" + integrity sha512-VJPP6A3BhxsLQzEfKPz3alCiT0nMqeM75P/reT1jsRxZsOCJ8vFn7g2c8aH2bEIcCqOWeUaaxVDuj8ghbzByUw== dependencies: "@adobe/spectrum-css-workflow-icons" "^1.2.1" "@spectrum-css/actionbutton" "^1.0.1" @@ -1215,14 +1216,14 @@ svelte-flatpickr "^3.1.0" svelte-portal "^1.0.0" -"@budibase/client@^0.9.79-alpha.4": - version "0.9.79" - resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.79.tgz#d1c8d51e9121f81902cfb31d3b685c8061f272a2" - integrity sha512-//Yqm5Qki6BmBe5W2Tz8GONdkFjdD1jkIU7pcLYKqdZJWEQIrX6T/xNvYvZVhw7Dx5bwSZRjFwzm7jLoiyHBIA== +"@budibase/client@^0.9.80-alpha.7": + version "0.9.80-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.80-alpha.7.tgz#9d2e98b90cd9fdcfc659826d19b5dc206cdcfe7d" + integrity sha512-szLz2JpWI9ZMyVz7IPap1fQ7e+uphuthOkOsERplmq4EXbv914/YILdEfUm01s4aeOEOdkeogz31t8t75es6Dg== dependencies: - "@budibase/bbui" "^0.9.79" - "@budibase/standard-components" "^0.9.79" - "@budibase/string-templates" "^0.9.79" + "@budibase/bbui" "^0.9.80-alpha.7" + "@budibase/standard-components" "^0.9.80-alpha.7" + "@budibase/string-templates" "^0.9.80-alpha.7" regexparam "^1.3.0" shortid "^2.2.15" svelte-spa-router "^3.0.5" @@ -1255,24 +1256,26 @@ to-gfm-code-block "^0.1.1" year "^0.2.1" -"@budibase/standard-components@^0.9.79", "@budibase/standard-components@^0.9.79-alpha.4": - version "0.9.79" - resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.79.tgz#24206642e0cdc655ea3a99ed5e9402ec4f6b3ba8" - integrity sha512-ZWhmBZ1iG+CjGMEvT/jtugMMgA1n88UYcOfP3BSP2P3eA16DubyU9hH9OyJHbGPzDHLoBF6vuS/5ZPZCkOKppw== +"@budibase/standard-components@^0.9.80-alpha.7": + version "0.9.80-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.80-alpha.7.tgz#17f13a25bfcda873f44d1460493325adcfe6f188" + integrity sha512-ohEVqhRxp2FeOlEnJtfBhyqtwmRGI/qPGs0K9FQfLQglMYJtPN5FgMrJ1gtN0W3zn7TOfNFnTcQIxIdLxSLwyA== dependencies: - "@budibase/bbui" "^0.9.79" + "@budibase/bbui" "^0.9.80-alpha.7" + "@spectrum-css/card" "^3.0.3" "@spectrum-css/link" "^3.1.3" "@spectrum-css/page" "^3.0.1" + "@spectrum-css/typography" "^3.0.2" "@spectrum-css/vars" "^3.0.1" apexcharts "^3.22.1" dayjs "^1.10.5" svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/string-templates@^0.9.79", "@budibase/string-templates@^0.9.79-alpha.4": - version "0.9.79" - resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.79.tgz#bb75a7433a7cfda1fc488283f35e47879b799fcc" - integrity sha512-hkAne5mx7mj8+osXFt45VwgLKSa94uQOGOb4R8uv9WNzvk4RzcjBfRzJxggv29FUemItrAeZpSh+Um6yugFI+w== +"@budibase/string-templates@^0.9.80-alpha.7": + version "0.9.80-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.80-alpha.7.tgz#10b06fc8652c00065f8928caebfcd0d143660078" + integrity sha512-lD3BSWXW6PrdAbZcpVXSsr/fA8NdwvQ8W7T4chQ661UUMKVOYLnGwAvvAOArGpkdzSOAfSEuzgIB0+pBc92qWQ== dependencies: "@budibase/handlebars-helpers" "^0.11.4" dayjs "^1.10.4" @@ -2111,6 +2114,11 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/buttongroup/-/buttongroup-3.0.3.tgz#719d868845ac9d2c4f939c1b9f6044507902d5aa" integrity sha512-eXl8U4QWMWXqyTu654FdQdEGnmczgOYlpIFSHyCMVjhtPqZp2xwnLFiGh6LKw+bLio6eeOZ0L+vpk1GcoYqgkw== +"@spectrum-css/card@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@spectrum-css/card/-/card-3.0.3.tgz#56b2e2da6b80c1583228baa279de7407383bfb6b" + integrity sha512-+oKLUI2a0QmQP9EzySeq/G4FpUkkdaDNbuEbqCj2IkPMc/2v/nwzsPhh1fj2UIghGAiiUwXfPpzax1e8fyhQUg== + "@spectrum-css/checkbox@^3.0.2": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.3.tgz#8577067fc8b97e4609f92bd242364937a533a7bb" @@ -2270,7 +2278,7 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/treeview/-/treeview-3.0.3.tgz#aeda5175158b9f8d7529cb2b394428eb2a428046" integrity sha512-D5gGzZC/KtRArdx86Mesc9+99W9nTbUOeyYGqoJoAfJSOttoT6Tk5CrDvlCmAqjKf5rajemAkGri1ChqvUIwkw== -"@spectrum-css/typography@^3.0.1": +"@spectrum-css/typography@^3.0.1", "@spectrum-css/typography@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-3.0.2.tgz#ea3ca0a60e18064527819d48c8c4364cab4fcd38" integrity sha512-5ZOLmQe0edzsDMyhghUd4hBb5uxGsFrxzf+WasfcUw9klSfTsRZ09n1BsaaWbgrLjlMQ+EEHS46v5VNo0Ms2CA== @@ -2292,6 +2300,17 @@ dependencies: defer-to-connect "^1.0.1" +"@techpass/passport-openidconnect@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@techpass/passport-openidconnect/-/passport-openidconnect-0.3.0.tgz#a60b2bbf3f262649a5a02d5d186219944acc3010" + integrity sha512-bVsPwl66s7J7GHxTPlW/RJYhZol9SshNznQsx83OOh9G+JWFGoeWxh+xbX+FTdJNoUvGIGbJnpWPY2wC6NOHPw== + dependencies: + base64url "^3.0.1" + oauth "^0.9.15" + passport-strategy "^1.0.0" + request "^2.88.0" + webfinger "^0.4.2" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -3286,7 +3305,7 @@ base64-js@^1.0.2, base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base64url@3.x.x: +base64url@3.x.x, base64url@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== @@ -8707,7 +8726,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -oauth@0.9.x: +oauth@0.9.x, oauth@^0.9.15: version "0.9.15" resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE= @@ -10081,7 +10100,7 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -"request@>= 2.52.0", request@^2.72.0, request@^2.74.0, request@^2.87.0: +"request@>= 2.52.0", request@^2.72.0, request@^2.74.0, request@^2.87.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -10205,7 +10224,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -10290,7 +10309,7 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= -sax@>=0.6.0, sax@^1.2.4: +sax@>=0.1.1, sax@>=0.6.0, sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -10745,6 +10764,11 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +step@0.0.x: + version "0.0.6" + resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" + integrity sha1-FD54SaXX0/SgiP4pr5SRUhbu7eI= + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -11600,11 +11624,6 @@ untildify@^4.0.0: resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -update-dotenv@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/update-dotenv/-/update-dotenv-1.1.1.tgz#17146f302f216c3c92419d5a327a45be910050ca" - integrity sha512-3cIC18In/t0X/yH793c00qqxcKD8jVCgNOPif/fGQkFpYMGecM9YAc+kaAKXuZsM2dE9I9wFI7KvAuNX22SGMQ== - update-notifier@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" @@ -11786,6 +11805,14 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +webfinger@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/webfinger/-/webfinger-0.4.2.tgz#3477a6d97799461896039fcffc650b73468ee76d" + integrity sha1-NHem2XeZRhiWA5/P/GULc0aO520= + dependencies: + step "0.0.x" + xml2js "0.1.x" + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -11992,6 +12019,13 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= +xml2js@0.1.x: + version "0.1.14" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" + integrity sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw= + dependencies: + sax ">=0.1.1" + xml2js@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" diff --git a/packages/worker/package.json b/packages/worker/package.json index 298c29a084..8b43253bb3 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -16,9 +16,7 @@ "build:docker": "docker build . -t worker-service", "dev:stack:init": "node ./scripts/dev/manage.js init", "dev:builder": "npm run dev:stack:init && nodemon src/index.js", - "test": "jest --runInBand", - "multi:enable": "node scripts/multiTenancy.js enable", - "multi:disable": "node scripts/multiTenancy.js disable" + "test": "jest --runInBand" }, "author": "Budibase", "license": "AGPL-3.0-or-later", @@ -54,8 +52,7 @@ "jest": "^26.6.3", "nodemon": "^2.0.7", "pouchdb-adapter-memory": "^7.2.2", - "supertest": "^6.1.3", - "update-dotenv": "^1.1.1" + "supertest": "^6.1.3" }, "jest": { "testEnvironment": "node", diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js index 281c677ed7..b9d28b6278 100644 --- a/packages/worker/scripts/dev/manage.js +++ b/packages/worker/scripts/dev/manage.js @@ -4,28 +4,26 @@ const fs = require("fs") async function init() { const envFilePath = path.join(process.cwd(), ".env") - if (!fs.existsSync(envFilePath)) { - const envFileJson = { - SELF_HOSTED: 1, - PORT: 4002, - CLUSTER_PORT: 10000, - JWT_SECRET: "testsecret", - INTERNAL_API_KEY: "budibase", - MINIO_ACCESS_KEY: "budibase", - MINIO_SECRET_KEY: "budibase", - REDIS_URL: "localhost:6379", - REDIS_PASSWORD: "budibase", - MINIO_URL: "http://localhost:10000/", - COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", - // empty string is false - MULTI_TENANCY: "", - } - let envFile = "" - Object.keys(envFileJson).forEach(key => { - envFile += `${key}=${envFileJson[key]}\n` - }) - fs.writeFileSync(envFilePath, envFile) + const envFileJson = { + SELF_HOSTED: 1, + PORT: 4002, + CLUSTER_PORT: 10000, + JWT_SECRET: "testsecret", + INTERNAL_API_KEY: "budibase", + MINIO_ACCESS_KEY: "budibase", + MINIO_SECRET_KEY: "budibase", + COUCH_DB_USER: "budibase", + COUCH_DB_PASSWORD: "budibase", + REDIS_URL: "localhost:6379", + REDIS_PASSWORD: "budibase", + MINIO_URL: "http://localhost:10000/", + COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", } + let envFile = "" + Object.keys(envFileJson).forEach(key => { + envFile += `${key}=${envFileJson[key]}\n` + }) + fs.writeFileSync(envFilePath, envFile) } // if more than init required use this to determine the command type diff --git a/packages/worker/scripts/jestSetup.js b/packages/worker/scripts/jestSetup.js index 374edfb946..07648f693f 100644 --- a/packages/worker/scripts/jestSetup.js +++ b/packages/worker/scripts/jestSetup.js @@ -3,4 +3,3 @@ const env = require("../src/environment") env._set("NODE_ENV", "jest") env._set("JWT_SECRET", "test-jwtsecret") env._set("LOG_LEVEL", "silent") -env._set("MULTI_TENANCY", true) diff --git a/packages/worker/scripts/multiTenancy.js b/packages/worker/scripts/multiTenancy.js deleted file mode 100644 index 3921a78979..0000000000 --- a/packages/worker/scripts/multiTenancy.js +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env node -const updateDotEnv = require("update-dotenv") - -const arg = process.argv.slice(2)[0] - -updateDotEnv({ - MULTI_TENANCY: arg === "enable" ? "1" : "", -}).then(() => console.log("Updated worker!")) diff --git a/packages/worker/src/api/controllers/global/auth.js b/packages/worker/src/api/controllers/admin/auth.js similarity index 82% rename from packages/worker/src/api/controllers/global/auth.js rename to packages/worker/src/api/controllers/admin/auth.js index b08d51e642..3cdfc1b774 100644 --- a/packages/worker/src/api/controllers/global/auth.js +++ b/packages/worker/src/api/controllers/admin/auth.js @@ -2,27 +2,15 @@ const authPkg = require("@budibase/auth") const { google } = require("@budibase/auth/src/middleware") const { oidc } = require("@budibase/auth/src/middleware") const { Configs, EmailTemplatePurpose } = require("../../../constants") +const CouchDB = require("../../../db") const { sendEmail, isEmailConfigured } = require("../../../utilities/email") const { setCookie, getCookie, clearCookie, getGlobalUserByEmail, hash } = authPkg.utils const { Cookies } = authPkg.constants const { passport } = authPkg.auth const { checkResetPasswordCode } = require("../../../utilities/redis") -const { - getGlobalDB, - getTenantId, - isMultiTenant, -} = require("@budibase/auth/tenancy") -const env = require("../../../environment") -function googleCallbackUrl() { - let callbackUrl = `/api/global/auth` - if (isMultiTenant()) { - callbackUrl += `/${getTenantId()}` - } - callbackUrl += `/google/callback` - return callbackUrl -} +const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name async function authInternal(ctx, user, err = null, info = null) { if (err) { @@ -78,7 +66,6 @@ exports.reset = async ctx => { }) } } catch (err) { - console.log(err) // don't throw any kind of error to the user, this might give away something } ctx.body = { @@ -93,7 +80,7 @@ exports.resetUpdate = async ctx => { const { resetCode, password } = ctx.request.body try { const userId = await checkResetPasswordCode(resetCode) - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const user = await db.get(userId) user.password = await hash(password) await db.put(user) @@ -115,14 +102,12 @@ exports.logout = async ctx => { * On a successful login, you will be redirected to the googleAuth callback route. */ exports.googlePreAuth = async (ctx, next) => { - const db = getGlobalDB() - let callbackUrl = googleCallbackUrl() - + const db = new CouchDB(GLOBAL_DB) const config = await authPkg.db.getScopedConfig(db, { type: Configs.GOOGLE, - workspace: ctx.query.workspace, + group: ctx.query.group, }) - const strategy = await google.strategyFactory(config, callbackUrl) + const strategy = await google.strategyFactory(config) return passport.authenticate(strategy, { scope: ["profile", "email"], @@ -130,14 +115,13 @@ exports.googlePreAuth = async (ctx, next) => { } exports.googleAuth = async (ctx, next) => { - const db = getGlobalDB() - const callbackUrl = googleCallbackUrl() + const db = new CouchDB(GLOBAL_DB) const config = await authPkg.db.getScopedConfig(db, { type: Configs.GOOGLE, - workspace: ctx.query.workspace, + group: ctx.query.group, }) - const strategy = await google.strategyFactory(config, callbackUrl) + const strategy = await google.strategyFactory(config) return passport.authenticate( strategy, @@ -151,7 +135,8 @@ exports.googleAuth = async (ctx, next) => { } async function oidcStrategyFactory(ctx, configId) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) + const config = await authPkg.db.getScopedConfig(db, { type: Configs.OIDC, group: ctx.query.group, @@ -159,12 +144,9 @@ async function oidcStrategyFactory(ctx, configId) { const chosenConfig = config.configs.filter(c => c.uuid === configId)[0] - const protocol = env.NODE_ENV === "production" ? "https" : "http" - let callbackUrl = `${protocol}://${ctx.host}/api/global/auth` - if (isMultiTenant()) { - callbackUrl += `/${getTenantId()}` - } - callbackUrl += `/oidc/callback` + // require https callback in production + const protocol = process.env.NODE_ENV === "production" ? "https" : "http" + const callbackUrl = `${protocol}://${ctx.host}/api/admin/auth/oidc/callback` return oidc.strategyFactory(chosenConfig, callbackUrl) } diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/admin/configs.js similarity index 74% rename from packages/worker/src/api/controllers/global/configs.js rename to packages/worker/src/api/controllers/admin/configs.js index 8b4807b684..78caa817b2 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/admin/configs.js @@ -1,25 +1,28 @@ +const CouchDB = require("../../../db") const { generateConfigID, + StaticDatabases, getConfigParams, getGlobalUserParams, getScopedFullConfig, - getAllApps, -} = require("@budibase/auth/db") +} = require("@budibase/auth").db const { Configs } = require("../../../constants") const email = require("../../../utilities/email") const { upload, ObjectStoreBuckets } = require("@budibase/auth").objectStore -const CouchDB = require("../../../db") -const { getGlobalDB } = require("@budibase/auth/tenancy") + +const APP_PREFIX = "app_" + +const GLOBAL_DB = StaticDatabases.GLOBAL.name exports.save = async function (ctx) { - const db = getGlobalDB() - const { type, workspace, user, config } = ctx.request.body + const db = new CouchDB(GLOBAL_DB) + const { type, group, user, config } = ctx.request.body // Config does not exist yet if (!ctx.request.body._id) { ctx.request.body._id = generateConfigID({ type, - workspace, + group, user, }) } @@ -48,7 +51,7 @@ exports.save = async function (ctx) { } exports.fetch = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const response = await db.allDocs( getConfigParams( { type: ctx.params.type }, @@ -62,19 +65,17 @@ exports.fetch = async function (ctx) { /** * Gets the most granular config for a particular configuration type. - * The hierarchy is type -> workspace -> user. + * The hierarchy is type -> group -> user. */ exports.find = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) - const { userId, workspaceId } = ctx.query - if (workspaceId && userId) { - const workspace = await db.get(workspaceId) - const userInWorkspace = workspace.users.some( - workspaceUser => workspaceUser === userId - ) - if (!ctx.user.admin && !userInWorkspace) { - ctx.throw(400, `User is not in specified workspace: ${workspace}.`) + const { userId, groupId } = ctx.query + if (groupId && userId) { + const group = await db.get(groupId) + const userInGroup = group.users.some(groupUser => groupUser === userId) + if (!ctx.user.admin && !userInGroup) { + ctx.throw(400, `User is not in specified group: ${group}.`) } } @@ -83,7 +84,7 @@ exports.find = async function (ctx) { const scopedConfig = await getScopedFullConfig(db, { type: ctx.params.type, user: userId, - workspace: workspaceId, + group: groupId, }) if (scopedConfig) { @@ -98,7 +99,7 @@ exports.find = async function (ctx) { } exports.publicOidc = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) try { // Find the config with the most granular scope based on context const oidcConfig = await getScopedFullConfig(db, { @@ -108,11 +109,14 @@ exports.publicOidc = async function (ctx) { if (!oidcConfig) { ctx.body = {} } else { - ctx.body = oidcConfig.config.configs.map(config => ({ - logo: config.logo, - name: config.name, - uuid: config.uuid, - })) + const partialOidcCofig = oidcConfig.config.configs.map(config => { + return { + logo: config.logo, + name: config.name, + uuid: config.uuid, + } + }) + ctx.body = partialOidcCofig } } catch (err) { ctx.throw(err.status, err) @@ -120,7 +124,7 @@ exports.publicOidc = async function (ctx) { } exports.publicSettings = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) try { // Find the config with the most granular scope based on context @@ -136,7 +140,7 @@ exports.publicSettings = async function (ctx) { type: Configs.OIDC, }) - let config + let config = {} if (!publicConfig) { config = { config: {}, @@ -147,16 +151,18 @@ exports.publicSettings = async function (ctx) { // google button flag if (googleConfig && googleConfig.config) { - // activated by default for configs pre-activated flag - config.config.google = - googleConfig.config.activated == null || googleConfig.config.activated + const googleActivated = + googleConfig.config.activated == undefined || // activated by default for configs pre-activated flag + googleConfig.config.activated + config.config.google = googleActivated } else { config.config.google = false } // oidc button flag if (oidcConfig && oidcConfig.config) { - config.config.oidc = oidcConfig.config.configs[0].activated + const oidcActivated = oidcConfig.config.configs[0].activated + config.config.oidc = oidcActivated } else { config.config.oidc = false } @@ -185,7 +191,7 @@ exports.upload = async function (ctx) { // add to configuration structure // TODO: right now this only does a global level - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) let cfgStructure = await getScopedFullConfig(db, { type }) if (!cfgStructure) { cfgStructure = { @@ -205,7 +211,7 @@ exports.upload = async function (ctx) { } exports.destroy = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const { id, rev } = ctx.params try { @@ -217,13 +223,14 @@ exports.destroy = async function (ctx) { } exports.configChecklist = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) try { // TODO: Watch get started video // Apps exist - const apps = await getAllApps(CouchDB) + let allDbs = await CouchDB.allDbs() + const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX)) // They have set up SMTP const smtpConfig = await getScopedFullConfig(db, { @@ -239,7 +246,7 @@ exports.configChecklist = async function (ctx) { const oidcConfig = await getScopedFullConfig(db, { type: Configs.OIDC, }) - // They have set up an global user + // They have set up an admin user const users = await db.allDocs( getGlobalUserParams(null, { include_docs: true, @@ -248,7 +255,7 @@ exports.configChecklist = async function (ctx) { const adminUser = users.rows.some(row => row.doc.admin) ctx.body = { - apps: apps.length, + apps: appDbNames.length, smtp: !!smtpConfig, adminUser, sso: !!googleConfig || !!oidcConfig, diff --git a/packages/worker/src/api/controllers/global/email.js b/packages/worker/src/api/controllers/admin/email.js similarity index 58% rename from packages/worker/src/api/controllers/global/email.js rename to packages/worker/src/api/controllers/admin/email.js index 57b78a6d7a..6e16fd060c 100644 --- a/packages/worker/src/api/controllers/global/email.js +++ b/packages/worker/src/api/controllers/admin/email.js @@ -1,16 +1,19 @@ const { sendEmail } = require("../../../utilities/email") -const { getGlobalDB } = require("@budibase/auth/tenancy") +const CouchDB = require("../../../db") +const authPkg = require("@budibase/auth") + +const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name exports.sendEmail = async ctx => { - let { workspaceId, email, userId, purpose, contents, from, subject } = + const { groupId, email, userId, purpose, contents, from, subject } = ctx.request.body let user if (userId) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) user = await db.get(userId) } const response = await sendEmail(email, purpose, { - workspaceId, + groupId, user, contents, from, diff --git a/packages/worker/src/api/controllers/global/workspaces.js b/packages/worker/src/api/controllers/admin/groups.js similarity index 53% rename from packages/worker/src/api/controllers/global/workspaces.js rename to packages/worker/src/api/controllers/admin/groups.js index 95a1ec296d..330fb38282 100644 --- a/packages/worker/src/api/controllers/global/workspaces.js +++ b/packages/worker/src/api/controllers/admin/groups.js @@ -1,17 +1,20 @@ -const { getWorkspaceParams, generateWorkspaceID } = require("@budibase/auth/db") -const { getGlobalDB } = require("@budibase/auth/tenancy") +const CouchDB = require("../../../db") +const { getGroupParams, generateGroupID, StaticDatabases } = + require("@budibase/auth").db + +const GLOBAL_DB = StaticDatabases.GLOBAL.name exports.save = async function (ctx) { - const db = getGlobalDB() - const workspaceDoc = ctx.request.body + const db = new CouchDB(GLOBAL_DB) + const groupDoc = ctx.request.body - // workspace does not exist yet - if (!workspaceDoc._id) { - workspaceDoc._id = generateWorkspaceID() + // Group does not exist yet + if (!groupDoc._id) { + groupDoc._id = generateGroupID() } try { - const response = await db.post(workspaceDoc) + const response = await db.post(groupDoc) ctx.body = { _id: response.id, _rev: response.rev, @@ -22,9 +25,9 @@ exports.save = async function (ctx) { } exports.fetch = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const response = await db.allDocs( - getWorkspaceParams(undefined, { + getGroupParams(undefined, { include_docs: true, }) ) @@ -32,7 +35,7 @@ exports.fetch = async function (ctx) { } exports.find = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) try { ctx.body = await db.get(ctx.params.id) } catch (err) { @@ -41,12 +44,12 @@ exports.find = async function (ctx) { } exports.destroy = async function (ctx) { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const { id, rev } = ctx.params try { await db.remove(id, rev) - ctx.body = { message: "Workspace deleted successfully" } + ctx.body = { message: "Group deleted successfully" } } catch (err) { ctx.throw(err.status, err) } diff --git a/packages/worker/src/api/controllers/global/roles.js b/packages/worker/src/api/controllers/admin/roles.js similarity index 90% rename from packages/worker/src/api/controllers/global/roles.js rename to packages/worker/src/api/controllers/admin/roles.js index 1aae07241e..3cd99f8c4f 100644 --- a/packages/worker/src/api/controllers/global/roles.js +++ b/packages/worker/src/api/controllers/admin/roles.js @@ -7,9 +7,8 @@ const { const CouchDB = require("../../../db") exports.fetch = async ctx => { - const tenantId = ctx.user.tenantId // always use the dev apps as they'll be most up to date (true) - const apps = await getAllApps(CouchDB, { tenantId, all: true }) + const apps = await getAllApps({ CouchDB, all: true }) const promises = [] for (let app of apps) { // use dev app IDs diff --git a/packages/worker/src/api/controllers/global/sessions.js b/packages/worker/src/api/controllers/admin/sessions.js similarity index 100% rename from packages/worker/src/api/controllers/global/sessions.js rename to packages/worker/src/api/controllers/admin/sessions.js diff --git a/packages/worker/src/api/controllers/global/templates.js b/packages/worker/src/api/controllers/admin/templates.js similarity index 86% rename from packages/worker/src/api/controllers/global/templates.js rename to packages/worker/src/api/controllers/admin/templates.js index 0dc2b8abab..ab9c52cb5a 100644 --- a/packages/worker/src/api/controllers/global/templates.js +++ b/packages/worker/src/api/controllers/admin/templates.js @@ -1,14 +1,16 @@ -const { generateTemplateID } = require("@budibase/auth/db") +const { generateTemplateID, StaticDatabases } = require("@budibase/auth").db +const CouchDB = require("../../../db") const { TemplateMetadata, TemplateBindings, GLOBAL_OWNER, } = require("../../../constants") const { getTemplates } = require("../../../constants/templates") -const { getGlobalDB } = require("@budibase/auth/tenancy") + +const GLOBAL_DB = StaticDatabases.GLOBAL.name exports.save = async ctx => { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) let template = ctx.request.body if (!template.ownerId) { template.ownerId = GLOBAL_OWNER @@ -68,7 +70,7 @@ exports.find = async ctx => { } exports.destroy = async ctx => { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) await db.remove(ctx.params.id, ctx.params.rev) ctx.message = `Template ${ctx.params.id} deleted.` ctx.status = 200 diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/admin/users.js similarity index 64% rename from packages/worker/src/api/controllers/global/users.js rename to packages/worker/src/api/controllers/admin/users.js index 24b00fe3a6..f524379266 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/admin/users.js @@ -1,30 +1,17 @@ -const { - generateGlobalUserID, - getGlobalUserParams, - - StaticDatabases, -} = require("@budibase/auth/db") +const CouchDB = require("../../../db") +const { generateGlobalUserID, getGlobalUserParams, StaticDatabases } = + require("@budibase/auth").db const { hash, getGlobalUserByEmail } = require("@budibase/auth").utils const { UserStatus, EmailTemplatePurpose } = require("../../../constants") -const { DEFAULT_TENANT_ID } = require("@budibase/auth/constants") const { checkInviteCode } = require("../../../utilities/redis") const { sendEmail } = require("../../../utilities/email") const { user: userCache } = require("@budibase/auth/cache") const { invalidateSessions } = require("@budibase/auth/sessions") -const CouchDB = require("../../../db") -const env = require("../../../environment") -const { - getGlobalDB, - getTenantId, - doesTenantExist, - tryAddTenant, - updateTenantId, -} = require("@budibase/auth/tenancy") -const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name +const GLOBAL_DB = StaticDatabases.GLOBAL.name async function allUsers() { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const response = await db.allDocs( getGlobalUserParams(null, { include_docs: true, @@ -33,21 +20,16 @@ async function allUsers() { return response.rows.map(row => row.doc) } -async function saveUser(user, tenantId) { - if (!tenantId) { - throw "No tenancy specified." - } - // need to set the context for this request, as specified - updateTenantId(tenantId) - // specify the tenancy incase we're making a new admin user (public) - const db = getGlobalDB(tenantId) - let { email, password, _id } = user +exports.save = async ctx => { + const db = new CouchDB(GLOBAL_DB) + const { email, password, _id } = ctx.request.body + // make sure another user isn't using the same email let dbUser if (email) { dbUser = await getGlobalUserByEmail(email) if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { - throw "Email address already in use." + ctx.throw(400, "Email address already in use.") } } else { dbUser = await db.get(_id) @@ -60,16 +42,14 @@ async function saveUser(user, tenantId) { } else if (dbUser) { hashedPassword = dbUser.password } else { - throw "Password must be specified." + ctx.throw(400, "Password must be specified.") } - _id = _id || generateGlobalUserID() - user = { + let user = { ...dbUser, - ...user, - _id, + ...ctx.request.body, + _id: _id || generateGlobalUserID(), password: hashedPassword, - tenantId, } // make sure the roles object is always present if (!user.roles) { @@ -84,37 +64,23 @@ async function saveUser(user, tenantId) { password: hashedPassword, ...user, }) - await tryAddTenant(tenantId, _id, email) await userCache.invalidateUser(response.id) - return { + ctx.body = { _id: response.id, _rev: response.rev, email, } } catch (err) { if (err.status === 409) { - throw "User exists already" + ctx.throw(400, "User exists already") } else { - throw err + ctx.throw(err.status, err) } } } -exports.save = async ctx => { - try { - ctx.body = await saveUser(ctx.request.body, getTenantId()) - } catch (err) { - ctx.throw(err.status || 400, err) - } -} - exports.adminUser = async ctx => { - const { email, password, tenantId } = ctx.request.body - if (await doesTenantExist(tenantId)) { - ctx.throw(403, "Organisation already exists.") - } - - const db = getGlobalDB(tenantId) + const db = new CouchDB(GLOBAL_DB) const response = await db.allDocs( getGlobalUserParams(null, { include_docs: true, @@ -122,13 +88,11 @@ exports.adminUser = async ctx => { ) if (response.rows.some(row => row.doc.admin)) { - ctx.throw( - 403, - "You cannot initialise once an global user has been created." - ) + ctx.throw(403, "You cannot initialise once an admin user has been created.") } - const user = { + const { email, password } = ctx.request.body + ctx.request.body = { email: email, password: password, roles: {}, @@ -138,17 +102,12 @@ exports.adminUser = async ctx => { admin: { global: true, }, - tenantId, - } - try { - ctx.body = await saveUser(user, tenantId) - } catch (err) { - ctx.throw(err.status || 400, err) } + await exports.save(ctx) } exports.destroy = async ctx => { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const dbUser = await db.get(ctx.params.id) await db.remove(dbUser._id, dbUser._rev) await userCache.invalidateUser(dbUser._id) @@ -160,8 +119,8 @@ exports.destroy = async ctx => { exports.removeAppRole = async ctx => { const { appId } = ctx.params - const db = getGlobalDB() - const users = await allUsers(ctx) + const db = new CouchDB(GLOBAL_DB) + const users = await allUsers() const bulk = [] const cacheInvalidations = [] for (let user of users) { @@ -190,7 +149,7 @@ exports.getSelf = async ctx => { } exports.updateSelf = async ctx => { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) const user = await db.get(ctx.user._id) if (ctx.request.body.password) { ctx.request.body.password = await hash(ctx.request.body.password) @@ -211,7 +170,7 @@ exports.updateSelf = async ctx => { // called internally by app server user fetch exports.fetch = async ctx => { - const users = await allUsers(ctx) + const users = await allUsers() // user hashed password shouldn't ever be returned for (let user of users) { if (user) { @@ -223,7 +182,7 @@ exports.fetch = async ctx => { // called internally by app server user find exports.find = async ctx => { - const db = getGlobalDB() + const db = new CouchDB(GLOBAL_DB) let user try { user = await db.get(ctx.params.id) @@ -237,38 +196,12 @@ exports.find = async ctx => { ctx.body = user } -exports.tenantLookup = async ctx => { - const id = ctx.params.id - // lookup, could be email or userId, either will return a doc - const db = new CouchDB(PLATFORM_INFO_DB) - let tenantId = null - try { - const doc = await db.get(id) - if (doc && doc.tenantId) { - tenantId = doc.tenantId - } - } catch (err) { - if (!env.MULTI_TENANCY) { - tenantId = DEFAULT_TENANT_ID - } else { - ctx.throw(400, "No tenant found.") - } - } - ctx.body = { - tenantId, - } -} - exports.invite = async ctx => { - let { email, userInfo } = ctx.request.body + const { email, userInfo } = ctx.request.body const existing = await getGlobalUserByEmail(email) if (existing) { ctx.throw(400, "Email address already in use.") } - if (!userInfo) { - userInfo = {} - } - userInfo.tenantId = getTenantId() await sendEmail(email, EmailTemplatePurpose.INVITATION, { subject: "{{ company }} platform invitation", info: userInfo, @@ -281,18 +214,18 @@ exports.invite = async ctx => { exports.inviteAccept = async ctx => { const { inviteCode, password, firstName, lastName } = ctx.request.body try { - // info is an extension of the user object that was stored by global + // info is an extension of the user object that was stored by admin const { email, info } = await checkInviteCode(inviteCode) - ctx.body = await saveUser( - { - firstName, - lastName, - password, - email, - ...info, - }, - info.tenantId - ) + // only pass through certain props for accepting + ctx.request.body = { + firstName, + lastName, + password, + email, + ...info, + } + // this will flesh out the body response + await exports.save(ctx) } catch (err) { ctx.throw(400, "Unable to create new user, invitation invalid.") } diff --git a/packages/worker/src/api/controllers/app.js b/packages/worker/src/api/controllers/app.js index a7b6c5032c..ff9692a5ec 100644 --- a/packages/worker/src/api/controllers/app.js +++ b/packages/worker/src/api/controllers/app.js @@ -1,12 +1,18 @@ -const { getAllApps } = require("@budibase/auth/db") +const { DocumentTypes } = require("@budibase/auth").db const CouchDB = require("../../db") +const APP_PREFIX = "app_" const URL_REGEX_SLASH = /\/|\\/g exports.getApps = async ctx => { - const tenantId = ctx.user.tenantId - const apps = await getAllApps(CouchDB, { tenantId }) + // allDbs call of CouchDB is very inaccurate in production + const allDbs = await CouchDB.allDbs() + const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX)) + const appPromises = appDbNames.map(db => + new CouchDB(db).get(DocumentTypes.APP_METADATA) + ) + const apps = await Promise.allSettled(appPromises) const body = {} for (let app of apps) { if (app.status !== "fulfilled") { diff --git a/packages/worker/src/api/controllers/system/flags.js b/packages/worker/src/api/controllers/system/flags.js deleted file mode 100644 index fdfc49afad..0000000000 --- a/packages/worker/src/api/controllers/system/flags.js +++ /dev/null @@ -1,7 +0,0 @@ -const env = require("../../../environment") - -exports.fetch = async ctx => { - ctx.body = { - multiTenancy: !!env.MULTI_TENANCY, - } -} diff --git a/packages/worker/src/api/controllers/system/tenants.js b/packages/worker/src/api/controllers/system/tenants.js deleted file mode 100644 index e053216dd9..0000000000 --- a/packages/worker/src/api/controllers/system/tenants.js +++ /dev/null @@ -1,33 +0,0 @@ -const CouchDB = require("../../../db") -const { StaticDatabases } = require("@budibase/auth/db") - -exports.exists = async ctx => { - const tenantId = ctx.request.params - const db = new CouchDB(StaticDatabases.PLATFORM_INFO.name) - let exists = false - try { - const tenantsDoc = await db.get(StaticDatabases.PLATFORM_INFO.docs.tenants) - if (tenantsDoc) { - exists = tenantsDoc.tenantIds.indexOf(tenantId) !== -1 - } - } catch (err) { - // if error it doesn't exist - } - ctx.body = { - exists, - } -} - -exports.fetch = async ctx => { - const db = new CouchDB(StaticDatabases.PLATFORM_INFO.name) - let tenants = [] - try { - const tenantsDoc = await db.get(StaticDatabases.PLATFORM_INFO.docs.tenants) - if (tenantsDoc) { - tenants = tenantsDoc.tenantIds - } - } catch (err) { - // if error it doesn't exist - } - ctx.body = tenants -} diff --git a/packages/worker/src/api/index.js b/packages/worker/src/api/index.js index 844690148f..39ae320cc6 100644 --- a/packages/worker/src/api/index.js +++ b/packages/worker/src/api/index.js @@ -2,51 +2,55 @@ const Router = require("@koa/router") const compress = require("koa-compress") const zlib = require("zlib") const { routes } = require("./routes") -const { buildAuthMiddleware, auditLog, buildTenancyMiddleware } = - require("@budibase/auth").auth +const { buildAuthMiddleware, auditLog } = require("@budibase/auth").auth const PUBLIC_ENDPOINTS = [ { - // this covers all of the POST auth routes - route: "/api/global/auth/:tenantId", + route: "/api/admin/users/init", method: "POST", }, { - // this covers all of the GET auth routes - route: "/api/global/auth/:tenantId", - method: "GET", - }, - { - // this covers all of the public config routes - route: "/api/global/configs/public", - method: "GET", - }, - { - route: "/api/global/configs/checklist", - method: "GET", - }, - { - route: "/api/global/users/init", + route: "/api/admin/users/invite/accept", method: "POST", }, { - route: "/api/global/users/invite/accept", + route: "/api/admin/auth", method: "POST", }, { - route: "api/system/flags", + route: "/api/admin/auth/google", method: "GET", }, -] - -const NO_TENANCY_ENDPOINTS = [ - ...PUBLIC_ENDPOINTS, { - route: "/api/system", - method: "ALL", + route: "/api/admin/auth/google/callback", + method: "GET", }, { - route: "/api/global/users/self", + route: "/api/admin/auth/oidc", + method: "GET", + }, + { + route: "/api/admin/auth/oidc/callback", + method: "GET", + }, + { + route: "/api/admin/auth/reset", + method: "POST", + }, + { + route: "/api/admin/configs/checklist", + method: "GET", + }, + { + route: "/api/apps", + method: "GET", + }, + { + route: "/api/admin/configs/public", + method: "GET", + }, + { + route: "/api/admin/configs/publicOidc", method: "GET", }, ] @@ -67,10 +71,9 @@ router ) .use("/health", ctx => (ctx.status = 200)) .use(buildAuthMiddleware(PUBLIC_ENDPOINTS)) - .use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS)) // for now no public access is allowed to worker (bar health check) .use((ctx, next) => { - if (!ctx.isAuthenticated && !ctx.publicEndpoint) { + if (!ctx.isAuthenticated) { ctx.throw(403, "Unauthorized - no public worker access") } return next() diff --git a/packages/worker/src/api/routes/admin/auth.js b/packages/worker/src/api/routes/admin/auth.js new file mode 100644 index 0000000000..9a7ef5ebac --- /dev/null +++ b/packages/worker/src/api/routes/admin/auth.js @@ -0,0 +1,45 @@ +const Router = require("@koa/router") +const authController = require("../../controllers/admin/auth") +const joiValidator = require("../../../middleware/joi-validator") +const Joi = require("joi") + +const router = Router() + +function buildAuthValidation() { + // prettier-ignore + return joiValidator.body(Joi.object({ + username: Joi.string().required(), + password: Joi.string().required(), + }).required().unknown(false)) +} + +function buildResetValidation() { + // prettier-ignore + return joiValidator.body(Joi.object({ + email: Joi.string().required(), + }).required().unknown(false)) +} + +function buildResetUpdateValidation() { + // prettier-ignore + return joiValidator.body(Joi.object({ + resetCode: Joi.string().required(), + password: Joi.string().required(), + }).required().unknown(false)) +} + +router + .post("/api/admin/auth", buildAuthValidation(), authController.authenticate) + .post("/api/admin/auth/reset", buildResetValidation(), authController.reset) + .post( + "/api/admin/auth/reset/update", + buildResetUpdateValidation(), + authController.resetUpdate + ) + .post("/api/admin/auth/logout", authController.logout) + .get("/api/admin/auth/google", authController.googlePreAuth) + .get("/api/admin/auth/google/callback", authController.googleAuth) + .get("/api/admin/auth/oidc/configs/:configId", authController.oidcPreAuth) + .get("/api/admin/auth/oidc/callback", authController.oidcAuth) + +module.exports = router diff --git a/packages/worker/src/api/routes/global/configs.js b/packages/worker/src/api/routes/admin/configs.js similarity index 82% rename from packages/worker/src/api/routes/global/configs.js rename to packages/worker/src/api/routes/admin/configs.js index f6cac4d3b2..6873d82757 100644 --- a/packages/worker/src/api/routes/global/configs.js +++ b/packages/worker/src/api/routes/admin/configs.js @@ -1,5 +1,5 @@ const Router = require("@koa/router") -const controller = require("../../controllers/global/configs") +const controller = require("../../controllers/admin/configs") const joiValidator = require("../../../middleware/joi-validator") const adminOnly = require("../../../middleware/adminOnly") const Joi = require("joi") @@ -37,6 +37,7 @@ function googleValidation() { return Joi.object({ clientID: Joi.string().required(), clientSecret: Joi.string().required(), + callbackURL: Joi.string().required(), activated: Joi.boolean().required(), }).unknown(true) } @@ -63,7 +64,7 @@ function buildConfigSaveValidation() { return joiValidator.body(Joi.object({ _id: Joi.string().optional(), _rev: Joi.string().optional(), - workspace: Joi.string().optional(), + group: Joi.string().optional(), type: Joi.string().valid(...Object.values(Configs)).required(), config: Joi.alternatives() .conditional("type", { @@ -96,24 +97,24 @@ function buildConfigGetValidation() { router .post( - "/api/global/configs", + "/api/admin/configs", adminOnly, buildConfigSaveValidation(), controller.save ) - .delete("/api/global/configs/:id/:rev", adminOnly, controller.destroy) - .get("/api/global/configs", controller.fetch) - .get("/api/global/configs/checklist", controller.configChecklist) + .delete("/api/admin/configs/:id/:rev", adminOnly, controller.destroy) + .get("/api/admin/configs", controller.fetch) + .get("/api/admin/configs/checklist", controller.configChecklist) .get( - "/api/global/configs/all/:type", + "/api/admin/configs/all/:type", buildConfigGetValidation(), controller.fetch ) - .get("/api/global/configs/public", controller.publicSettings) - .get("/api/global/configs/public/oidc", controller.publicOidc) - .get("/api/global/configs/:type", buildConfigGetValidation(), controller.find) + .get("/api/admin/configs/public", controller.publicSettings) + .get("/api/admin/configs/publicOidc", controller.publicOidc) + .get("/api/admin/configs/:type", buildConfigGetValidation(), controller.find) .post( - "/api/global/configs/upload/:type/:name", + "/api/admin/configs/upload/:type/:name", adminOnly, buildUploadValidation(), controller.upload diff --git a/packages/worker/src/api/routes/global/email.js b/packages/worker/src/api/routes/admin/email.js similarity index 80% rename from packages/worker/src/api/routes/global/email.js rename to packages/worker/src/api/routes/admin/email.js index fecbc02cd7..a36dc5de91 100644 --- a/packages/worker/src/api/routes/global/email.js +++ b/packages/worker/src/api/routes/admin/email.js @@ -1,5 +1,5 @@ const Router = require("@koa/router") -const controller = require("../../controllers/global/email") +const controller = require("../../controllers/admin/email") const { EmailTemplatePurpose } = require("../../../constants") const joiValidator = require("../../../middleware/joi-validator") const adminOnly = require("../../../middleware/adminOnly") @@ -12,15 +12,15 @@ function buildEmailSendValidation() { return joiValidator.body(Joi.object({ email: Joi.string().email(), purpose: Joi.string().valid(...Object.values(EmailTemplatePurpose)), - workspaceId: Joi.string().allow("", null), - from: Joi.string().allow("", null), + groupId: Joi.string().allow("", null), + fromt: Joi.string().allow("", null), contents: Joi.string().allow("", null), subject: Joi.string().allow("", null), }).required().unknown(true)) } router.post( - "/api/global/email/send", + "/api/admin/email/send", buildEmailSendValidation(), adminOnly, controller.sendEmail diff --git a/packages/worker/src/api/routes/global/workspaces.js b/packages/worker/src/api/routes/admin/groups.js similarity index 68% rename from packages/worker/src/api/routes/global/workspaces.js rename to packages/worker/src/api/routes/admin/groups.js index cab76b7763..4611e67079 100644 --- a/packages/worker/src/api/routes/global/workspaces.js +++ b/packages/worker/src/api/routes/admin/groups.js @@ -1,12 +1,12 @@ const Router = require("@koa/router") -const controller = require("../../controllers/global/workspaces") +const controller = require("../../controllers/admin/groups") const joiValidator = require("../../../middleware/joi-validator") const adminOnly = require("../../../middleware/adminOnly") const Joi = require("joi") const router = Router() -function buildWorkspaceSaveValidation() { +function buildGroupSaveValidation() { // prettier-ignore return joiValidator.body(Joi.object({ _id: Joi.string().optional(), @@ -26,13 +26,13 @@ function buildWorkspaceSaveValidation() { router .post( - "/api/global/workspaces", + "/api/admin/groups", adminOnly, - buildWorkspaceSaveValidation(), + buildGroupSaveValidation(), controller.save ) - .delete("/api/global/workspaces/:id", adminOnly, controller.destroy) - .get("/api/global/workspaces", controller.fetch) - .get("/api/global/workspaces/:id", controller.find) + .get("/api/admin/groups", controller.fetch) + .delete("/api/admin/groups/:id", adminOnly, controller.destroy) + .get("/api/admin/groups/:id", controller.find) module.exports = router diff --git a/packages/worker/src/api/routes/admin/roles.js b/packages/worker/src/api/routes/admin/roles.js new file mode 100644 index 0000000000..2deef6b3fe --- /dev/null +++ b/packages/worker/src/api/routes/admin/roles.js @@ -0,0 +1,11 @@ +const Router = require("@koa/router") +const controller = require("../../controllers/admin/roles") +const adminOnly = require("../../../middleware/adminOnly") + +const router = Router() + +router + .get("/api/admin/roles", adminOnly, controller.fetch) + .get("/api/admin/roles/:appId", adminOnly, controller.find) + +module.exports = router diff --git a/packages/worker/src/api/routes/admin/sessions.js b/packages/worker/src/api/routes/admin/sessions.js new file mode 100644 index 0000000000..9cf5f58f8b --- /dev/null +++ b/packages/worker/src/api/routes/admin/sessions.js @@ -0,0 +1,14 @@ +const Router = require("@koa/router") +const controller = require("../../controllers/admin/sessions") +const adminOnly = require("../../../middleware/adminOnly") + +const router = Router() + +router + .get("/api/admin/sessions", adminOnly, controller.fetch) + .get("/api/admin/sessions/self", controller.selfSessions) + .get("/api/admin/sessions/:userId", adminOnly, controller.find) + .delete("/api/admin/sessions/:userId", adminOnly, controller.invalidateUser) + .delete("/api/admin/sessions/self/:sessionId", controller.invalidateSession) + +module.exports = router diff --git a/packages/worker/src/api/routes/global/templates.js b/packages/worker/src/api/routes/admin/templates.js similarity index 66% rename from packages/worker/src/api/routes/global/templates.js rename to packages/worker/src/api/routes/admin/templates.js index e4580d444c..52ab24878b 100644 --- a/packages/worker/src/api/routes/global/templates.js +++ b/packages/worker/src/api/routes/admin/templates.js @@ -1,5 +1,5 @@ const Router = require("@koa/router") -const controller = require("../../controllers/global/templates") +const controller = require("../../controllers/admin/templates") const joiValidator = require("../../../middleware/joi-validator") const Joi = require("joi") const { TemplatePurpose, TemplateTypes } = require("../../../constants") @@ -21,17 +21,17 @@ function buildTemplateSaveValidation() { } router - .get("/api/global/template/definitions", controller.definitions) + .get("/api/admin/template/definitions", controller.definitions) .post( - "/api/global/template", + "/api/admin/template", adminOnly, buildTemplateSaveValidation(), controller.save ) - .get("/api/global/template", controller.fetch) - .get("/api/global/template/:type", controller.fetchByType) - .get("/api/global/template/:ownerId", controller.fetchByOwner) - .get("/api/global/template/:id", controller.find) - .delete("/api/global/template/:id/:rev", adminOnly, controller.destroy) + .get("/api/admin/template", controller.fetch) + .get("/api/admin/template/:type", controller.fetchByType) + .get("/api/admin/template/:ownerId", controller.fetchByOwner) + .get("/api/admin/template/:id", controller.find) + .delete("/api/admin/template/:id/:rev", adminOnly, controller.destroy) module.exports = router diff --git a/packages/worker/src/api/routes/global/users.js b/packages/worker/src/api/routes/admin/users.js similarity index 71% rename from packages/worker/src/api/routes/global/users.js rename to packages/worker/src/api/routes/admin/users.js index 8359835952..e302725232 100644 --- a/packages/worker/src/api/routes/global/users.js +++ b/packages/worker/src/api/routes/admin/users.js @@ -1,5 +1,5 @@ const Router = require("@koa/router") -const controller = require("../../controllers/global/users") +const controller = require("../../controllers/admin/users") const joiValidator = require("../../../middleware/joi-validator") const adminOnly = require("../../../middleware/adminOnly") const Joi = require("joi") @@ -11,7 +11,6 @@ function buildAdminInitValidation() { Joi.object({ email: Joi.string().required(), password: Joi.string().required(), - tenantId: Joi.string().required(), }) .required() .unknown(false) @@ -62,40 +61,39 @@ function buildInviteAcceptValidation() { router .post( - "/api/global/users", + "/api/admin/users", adminOnly, buildUserSaveValidation(), controller.save ) - .get("/api/global/users", adminOnly, controller.fetch) - .delete("/api/global/roles/:appId", adminOnly, controller.removeAppRole) - .delete("/api/global/users/:id", adminOnly, controller.destroy) - .get("/api/global/roles/:appId") + .get("/api/admin/users", adminOnly, controller.fetch) + .delete("/api/admin/roles/:appId", adminOnly, controller.removeAppRole) + .delete("/api/admin/users/:id", adminOnly, controller.destroy) + .get("/api/admin/roles/:appId") .post( - "/api/global/users/invite", + "/api/admin/users/invite", adminOnly, buildInviteValidation(), controller.invite ) - // non-global endpoints + // non-admin endpoints .post( - "/api/global/users/self", + "/api/admin/users/self", buildUserSaveValidation(true), controller.updateSelf ) .post( - "/api/global/users/invite/accept", + "/api/admin/users/invite/accept", buildInviteAcceptValidation(), controller.inviteAccept ) .post( - "/api/global/users/init", + "/api/admin/users/init", buildAdminInitValidation(), controller.adminUser ) - .get("/api/global/users/self", controller.getSelf) - .get("/api/global/users/tenant/:id", adminOnly, controller.tenantLookup) - // global endpoint but needs to come at end (blocks other endpoints otherwise) - .get("/api/global/users/:id", adminOnly, controller.find) + .get("/api/admin/users/self", controller.getSelf) + // admin endpoint but needs to come at end (blocks other endpoints otherwise) + .get("/api/admin/users/:id", adminOnly, controller.find) module.exports = router diff --git a/packages/worker/src/api/routes/global/auth.js b/packages/worker/src/api/routes/global/auth.js deleted file mode 100644 index f85d08057b..0000000000 --- a/packages/worker/src/api/routes/global/auth.js +++ /dev/null @@ -1,81 +0,0 @@ -const Router = require("@koa/router") -const authController = require("../../controllers/global/auth") -const joiValidator = require("../../../middleware/joi-validator") -const Joi = require("joi") -const { updateTenantId } = require("@budibase/auth/tenancy") - -const router = Router() - -function buildAuthValidation() { - // prettier-ignore - return joiValidator.body(Joi.object({ - username: Joi.string().required(), - password: Joi.string().required(), - }).required().unknown(false)) -} - -function buildResetValidation() { - // prettier-ignore - return joiValidator.body(Joi.object({ - email: Joi.string().required(), - }).required().unknown(false)) -} - -function buildResetUpdateValidation() { - // prettier-ignore - return joiValidator.body(Joi.object({ - resetCode: Joi.string().required(), - password: Joi.string().required(), - }).required().unknown(false)) -} - -function updateTenant(ctx, next) { - updateTenantId(ctx.params.tenantId) - return next() -} - -router - .post( - "/api/global/auth/:tenantId/login", - buildAuthValidation(), - updateTenant, - authController.authenticate - ) - .post( - "/api/global/auth/:tenantId/reset", - buildResetValidation(), - updateTenant, - authController.reset - ) - .post( - "/api/global/auth/:tenantId/reset/update", - buildResetUpdateValidation(), - updateTenant, - authController.resetUpdate - ) - .post("/api/global/auth/logout", authController.logout) - .get( - "/api/global/auth/:tenantId/google", - updateTenant, - authController.googlePreAuth - ) - .get( - "/api/global/auth/:tenantId/google/callback", - updateTenant, - authController.googleAuth - ) - .get( - "/api/global/auth/:tenantId/oidc/configs/:configId", - updateTenant, - authController.oidcPreAuth - ) - .get( - "/api/global/auth/:tenantId/oidc/callback", - updateTenant, - authController.oidcAuth - ) - // deprecated - used by the default system before tenancy - .get("/api/admin/auth/google/callback", authController.googleAuth) - .get("/api/admin/auth/oidc/callback", authController.oidcAuth) - -module.exports = router diff --git a/packages/worker/src/api/routes/global/roles.js b/packages/worker/src/api/routes/global/roles.js deleted file mode 100644 index c73fb317cf..0000000000 --- a/packages/worker/src/api/routes/global/roles.js +++ /dev/null @@ -1,11 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../../controllers/global/roles") -const adminOnly = require("../../../middleware/adminOnly") - -const router = Router() - -router - .get("/api/global/roles", adminOnly, controller.fetch) - .get("/api/global/roles/:appId", adminOnly, controller.find) - -module.exports = router diff --git a/packages/worker/src/api/routes/global/sessions.js b/packages/worker/src/api/routes/global/sessions.js deleted file mode 100644 index 5ba6747e68..0000000000 --- a/packages/worker/src/api/routes/global/sessions.js +++ /dev/null @@ -1,14 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../../controllers/global/sessions") -const adminOnly = require("../../../middleware/adminOnly") - -const router = Router() - -router - .get("/api/global/sessions", adminOnly, controller.fetch) - .get("/api/global/sessions/self", controller.selfSessions) - .get("/api/global/sessions/:userId", adminOnly, controller.find) - .delete("/api/global/sessions/:userId", adminOnly, controller.invalidateUser) - .delete("/api/global/sessions/self/:sessionId", controller.invalidateSession) - -module.exports = router diff --git a/packages/worker/src/api/routes/index.js b/packages/worker/src/api/routes/index.js index a4ed4d7da4..21ec324880 100644 --- a/packages/worker/src/api/routes/index.js +++ b/packages/worker/src/api/routes/index.js @@ -1,25 +1,21 @@ -const userRoutes = require("./global/users") -const configRoutes = require("./global/configs") -const workspaceRoutes = require("./global/workspaces") -const templateRoutes = require("./global/templates") -const emailRoutes = require("./global/email") -const authRoutes = require("./global/auth") -const roleRoutes = require("./global/roles") -const sessionRoutes = require("./global/sessions") -const flagRoutes = require("./system/flags") -const tenantsRoutes = require("./system/tenants") +const userRoutes = require("./admin/users") +const configRoutes = require("./admin/configs") +const groupRoutes = require("./admin/groups") +const templateRoutes = require("./admin/templates") +const emailRoutes = require("./admin/email") +const authRoutes = require("./admin/auth") +const roleRoutes = require("./admin/roles") +const sessionRoutes = require("./admin/sessions") const appRoutes = require("./app") exports.routes = [ configRoutes, userRoutes, - workspaceRoutes, + groupRoutes, authRoutes, appRoutes, templateRoutes, - tenantsRoutes, emailRoutes, sessionRoutes, roleRoutes, - flagRoutes, ] diff --git a/packages/worker/src/api/routes/system/flags.js b/packages/worker/src/api/routes/system/flags.js deleted file mode 100644 index f2f5c5712f..0000000000 --- a/packages/worker/src/api/routes/system/flags.js +++ /dev/null @@ -1,8 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../../controllers/system/flags") - -const router = Router() - -router.get("/api/system/flags", controller.fetch) - -module.exports = router diff --git a/packages/worker/src/api/routes/system/tenants.js b/packages/worker/src/api/routes/system/tenants.js deleted file mode 100644 index 223ba9f26e..0000000000 --- a/packages/worker/src/api/routes/system/tenants.js +++ /dev/null @@ -1,11 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../../controllers/system/tenants") -const adminOnly = require("../../../middleware/adminOnly") - -const router = Router() - -router - .get("/api/system/tenants/:tenantId/exists", controller.exists) - .get("/api/system/tenants", adminOnly, controller.fetch) - -module.exports = router diff --git a/packages/worker/src/api/routes/tests/auth.spec.js b/packages/worker/src/api/routes/tests/auth.spec.js index dacff30ce3..ceccf7edaf 100644 --- a/packages/worker/src/api/routes/tests/auth.spec.js +++ b/packages/worker/src/api/routes/tests/auth.spec.js @@ -1,11 +1,10 @@ const setup = require("./utilities") - -const TENANT_ID = "default" +const { Cookies } = require("@budibase/auth").constants jest.mock("nodemailer") const sendMailMock = setup.emailMock() -describe("/api/global/auth", () => { +describe("/api/admin/auth", () => { let request = setup.getRequest() let config = setup.getConfig() let code @@ -26,7 +25,7 @@ describe("/api/global/auth", () => { await config.saveSettingsConfig() await config.createUser("test@test.com") const res = await request - .post(`/api/global/auth/${TENANT_ID}/reset`) + .post(`/api/admin/auth/reset`) .send({ email: "test@test.com", }) @@ -36,14 +35,14 @@ describe("/api/global/auth", () => { expect(sendMailMock).toHaveBeenCalled() const emailCall = sendMailMock.mock.calls[0][0] // after this URL there should be a code - const parts = emailCall.html.split(`http://localhost:10000/builder/auth/reset?code=`) - code = parts[1].split("\"")[0].split("&")[0] + const parts = emailCall.html.split("http://localhost:10000/builder/auth/reset?code=") + code = parts[1].split("\"")[0] expect(code).toBeDefined() }) it("should allow resetting user password with code", async () => { const res = await request - .post(`/api/global/auth/${TENANT_ID}/reset/update`) + .post(`/api/admin/auth/reset/update`) .send({ password: "newpassword", resetCode: code, @@ -76,13 +75,13 @@ describe("/api/global/auth", () => { afterEach(() => { expect(strategyFactory).toBeCalledWith( chosenConfig, - `http://127.0.0.1:4003/api/global/auth/${TENANT_ID}/oidc/callback` // calculated url + `http://127.0.0.1:4003/api/admin/auth/oidc/callback` // calculated url ) }) - describe("oidc configs", () => { + describe("/api/admin/auth/oidc/configs", () => { it("should load strategy and delegate to passport", async () => { - await request.get(`/api/global/auth/${TENANT_ID}/oidc/configs/${configId}`) + await request.get(`/api/admin/auth/oidc/configs/${configId}`) expect(passportSpy).toBeCalledWith(mockStrategyReturn, { scope: ["profile", "email"], @@ -91,9 +90,9 @@ describe("/api/global/auth", () => { }) }) - describe("oidc callback", () => { + describe("/api/admin/auth/oidc/callback", () => { it("should load strategy and delegate to passport", async () => { - await request.get(`/api/global/auth/${TENANT_ID}/oidc/callback`) + await request.get(`/api/admin/auth/oidc/callback`) .set(config.getOIDConfigCookie(configId)) expect(passportSpy).toBeCalledWith(mockStrategyReturn, { diff --git a/packages/worker/src/api/routes/tests/configs.spec.js b/packages/worker/src/api/routes/tests/configs.spec.js index 10feb77b37..13ba2bd3bc 100644 --- a/packages/worker/src/api/routes/tests/configs.spec.js +++ b/packages/worker/src/api/routes/tests/configs.spec.js @@ -1,27 +1,30 @@ const setup = require("./utilities") // mock the email system +const sendMailMock = jest.fn() jest.mock("nodemailer") const nodemailer = require("nodemailer") nodemailer.createTransport.mockReturnValue({ verify: jest.fn() }) -describe("/api/global/configs/checklist", () => { +describe("/api/admin/configs/checklist", () => { let request = setup.getRequest() let config = setup.getConfig() beforeAll(async () => { - await config.init() + await config.init(false) }) afterAll(setup.afterAll) it("should return the correct checklist status based on the state of the budibase installation", async () => { + // initially configure settings + await config.saveAdminUser() await config.saveSmtpConfig() const res = await request - .get(`/api/global/configs/checklist`) + .get(`/api/admin/configs/checklist`) .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) diff --git a/packages/worker/src/api/routes/tests/email.spec.js b/packages/worker/src/api/routes/tests/email.spec.js index c8c93658f7..797b0326ed 100644 --- a/packages/worker/src/api/routes/tests/email.spec.js +++ b/packages/worker/src/api/routes/tests/email.spec.js @@ -1,6 +1,5 @@ const setup = require("./utilities") const { EmailTemplatePurpose } = require("../../../constants") -const { TENANT_ID } = require("./utilities/structures") // mock the email system const sendMailMock = jest.fn() @@ -11,7 +10,7 @@ nodemailer.createTransport.mockReturnValue({ verify: jest.fn() }) -describe("/api/global/email", () => { +describe("/api/admin/email", () => { let request = setup.getRequest() let config = setup.getConfig() @@ -26,11 +25,10 @@ describe("/api/global/email", () => { await config.saveSmtpConfig() await config.saveSettingsConfig() const res = await request - .post(`/api/global/email/send`) + .post(`/api/admin/email/send`) .send({ email: "test@test.com", purpose: EmailTemplatePurpose.INVITATION, - tenantId: TENANT_ID, }) .set(config.defaultHeaders()) .expect("Content-Type", /json/) diff --git a/packages/worker/src/api/routes/tests/realEmail.spec.js b/packages/worker/src/api/routes/tests/realEmail.spec.js index 845e31d911..acc0c7acc9 100644 --- a/packages/worker/src/api/routes/tests/realEmail.spec.js +++ b/packages/worker/src/api/routes/tests/realEmail.spec.js @@ -6,7 +6,7 @@ const fetch = require("node-fetch") // need a longer timeout for getting these jest.setTimeout(30000) -describe("/api/global/email", () => { +describe("/api/admin/email", () => { let request = setup.getRequest() let config = setup.getConfig() @@ -21,7 +21,7 @@ describe("/api/global/email", () => { await config.saveSettingsConfig() const user = await config.getUser("test@test.com") const res = await request - .post(`/api/global/email/send`) + .post(`/api/admin/email/send`) .send({ email: "test@test.com", purpose, diff --git a/packages/worker/src/api/routes/tests/users.spec.js b/packages/worker/src/api/routes/tests/users.spec.js index f03f9e60be..bf5b67ab1a 100644 --- a/packages/worker/src/api/routes/tests/users.spec.js +++ b/packages/worker/src/api/routes/tests/users.spec.js @@ -1,10 +1,9 @@ const setup = require("./utilities") -const { TENANT_ID } = require("./utilities/structures") jest.mock("nodemailer") const sendMailMock = setup.emailMock() -describe("/api/global/users", () => { +describe("/api/admin/users", () => { let request = setup.getRequest() let config = setup.getConfig() let code @@ -20,7 +19,7 @@ describe("/api/global/users", () => { await config.saveSmtpConfig() await config.saveSettingsConfig() const res = await request - .post(`/api/global/users/invite`) + .post(`/api/admin/users/invite`) .send({ email: "invite@test.com", }) @@ -32,13 +31,13 @@ describe("/api/global/users", () => { const emailCall = sendMailMock.mock.calls[0][0] // after this URL there should be a code const parts = emailCall.html.split("http://localhost:10000/builder/invite?code=") - code = parts[1].split("\"")[0].split("&")[0] + code = parts[1].split("\"")[0] expect(code).toBeDefined() }) it("should be able to create new user from invite", async () => { const res = await request - .post(`/api/global/users/invite/accept`) + .post(`/api/admin/users/invite/accept`) .send({ password: "newpassword", inviteCode: code, diff --git a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js index 7f84de6b7d..812dbe51e2 100644 --- a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js @@ -7,11 +7,6 @@ const { Configs, LOGO_URL } = require("../../../../constants") const { getGlobalUserByEmail } = require("@budibase/auth").utils const { createASession } = require("@budibase/auth/sessions") const { newid } = require("../../../../../../auth/src/hashing") -const { TENANT_ID } = require("./structures") -const auth = require("@budibase/auth") -const CouchDB = require("../../../../db") -const { doInTenant } = require("@budibase/auth/tenancy") -auth.init(CouchDB) class TestConfiguration { constructor(openServer = true) { @@ -33,7 +28,7 @@ class TestConfiguration { request.cookies = { set: () => {}, get: () => {} } request.config = { jwtSecret: env.JWT_SECRET } request.appId = this.appId - request.user = { appId: this.appId, tenantId: TENANT_ID } + request.user = { appId: this.appId } request.query = {} request.request = { body: config, @@ -41,9 +36,7 @@ class TestConfiguration { if (params) { request.params = params } - await doInTenant(TENANT_ID, () => { - return controlFunc(request) - }) + await controlFunc(request) return request.body } @@ -65,11 +58,8 @@ class TestConfiguration { null, controllers.users.save ) + await createASession("us_uuid1", "sessionid") } - await createASession("us_uuid1", { - sessionId: "sessionid", - tenantId: TENANT_ID, - }) } async end() { @@ -89,7 +79,6 @@ class TestConfiguration { _id: "us_uuid1", userId: "us_uuid1", sessionId: "sessionid", - tenantId: TENANT_ID, } const authToken = jwt.sign(user, env.JWT_SECRET) return { @@ -99,9 +88,7 @@ class TestConfiguration { } async getUser(email) { - return doInTenant(TENANT_ID, () => { - return getGlobalUserByEmail(email) - }) + return getGlobalUserByEmail(email) } async createUser(email = "test@test.com", password = "test") { @@ -165,6 +152,7 @@ class TestConfiguration { { type: Configs.GOOGLE, config: { + callbackURL: "http://somecallbackurl", clientID: "clientId", clientSecret: "clientSecret", }, @@ -243,7 +231,6 @@ class TestConfiguration { { email: "testuser@test.com", password: "test@test.com", - tenantId: TENANT_ID, }, null, controllers.users.adminUser diff --git a/packages/worker/src/api/routes/tests/utilities/controllers.js b/packages/worker/src/api/routes/tests/utilities/controllers.js index 45216ae634..b0d2441c0a 100644 --- a/packages/worker/src/api/routes/tests/utilities/controllers.js +++ b/packages/worker/src/api/routes/tests/utilities/controllers.js @@ -1,7 +1,7 @@ module.exports = { - email: require("../../../controllers/global/email"), - workspaces: require("../../../controllers/global/workspaces"), - config: require("../../../controllers/global/configs"), - templates: require("../../../controllers/global/templates"), - users: require("../../../controllers/global/users"), + email: require("../../../controllers/admin/email"), + groups: require("../../../controllers/admin/groups"), + config: require("../../../controllers/admin/configs"), + templates: require("../../../controllers/admin/templates"), + users: require("../../../controllers/admin/users"), } diff --git a/packages/worker/src/api/routes/tests/utilities/structures.js b/packages/worker/src/api/routes/tests/utilities/structures.js deleted file mode 100644 index 16701ac3d7..0000000000 --- a/packages/worker/src/api/routes/tests/utilities/structures.js +++ /dev/null @@ -1 +0,0 @@ -exports.TENANT_ID = "default" diff --git a/packages/worker/src/constants/index.js b/packages/worker/src/constants/index.js index a2bc818674..231ff37ee2 100644 --- a/packages/worker/src/constants/index.js +++ b/packages/worker/src/constants/index.js @@ -8,6 +8,10 @@ exports.UserStatus = { INACTIVE: "inactive", } +exports.Groups = { + ALL_USERS: "all_users", +} + exports.Configs = Configs exports.ConfigUploads = { diff --git a/packages/worker/src/constants/templates/index.js b/packages/worker/src/constants/templates/index.js index ac5427ba1f..c677f504c4 100644 --- a/packages/worker/src/constants/templates/index.js +++ b/packages/worker/src/constants/templates/index.js @@ -6,8 +6,8 @@ const { GLOBAL_OWNER, } = require("../index") const { join } = require("path") -const { getTemplateParams } = require("@budibase/auth/db") -const { getGlobalDB } = require("@budibase/auth/tenancy") +const CouchDB = require("../../db") +const { getTemplateParams, StaticDatabases } = require("@budibase/auth").db exports.EmailTemplates = { [EmailTemplatePurpose.PASSWORD_RECOVERY]: readStaticFile( @@ -50,7 +50,7 @@ exports.addBaseTemplates = (templates, type = null) => { } exports.getTemplates = async ({ ownerId, type, id } = {}) => { - const db = getGlobalDB() + const db = new CouchDB(StaticDatabases.GLOBAL.name) const response = await db.allDocs( getTemplateParams(ownerId || GLOBAL_OWNER, id, { include_docs: true, diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js index c42bc087e8..384230b9b3 100644 --- a/packages/worker/src/environment.js +++ b/packages/worker/src/environment.js @@ -17,7 +17,6 @@ if (!LOADED && isDev() && !isTest()) { } module.exports = { - NODE_ENV: process.env.NODE_ENV, SELF_HOSTED: process.env.SELF_HOSTED, PORT: process.env.PORT, CLUSTER_PORT: process.env.CLUSTER_PORT, @@ -31,7 +30,9 @@ module.exports = { REDIS_URL: process.env.REDIS_URL, REDIS_PASSWORD: process.env.REDIS_PASSWORD, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, - MULTI_TENANCY: process.env.MULTI_TENANCY, + /* TODO: to remove - once deployment removed */ + COUCH_DB_USERNAME: process.env.COUCH_DB_USERNAME, + COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, _set(key, value) { process.env[key] = value module.exports[key] = value diff --git a/packages/worker/src/utilities/email.js b/packages/worker/src/utilities/email.js index c32ff05cf5..dc13bb948a 100644 --- a/packages/worker/src/utilities/email.js +++ b/packages/worker/src/utilities/email.js @@ -1,13 +1,14 @@ const nodemailer = require("nodemailer") -const { getScopedConfig } = require("@budibase/auth/db") +const CouchDB = require("../db") +const { StaticDatabases, getScopedConfig } = require("@budibase/auth").db const { EmailTemplatePurpose, TemplateTypes, Configs } = require("../constants") const { getTemplateByPurpose } = require("../constants/templates") const { getSettingsTemplateContext } = require("./templates") const { processString } = require("@budibase/string-templates") const { getResetPasswordCode, getInviteCode } = require("../utilities/redis") -const { getGlobalDB } = require("@budibase/auth/tenancy") const TEST_MODE = false +const GLOBAL_DB = StaticDatabases.GLOBAL.name const TYPE = TemplateTypes.EMAIL const FULL_EMAIL_PURPOSES = [ @@ -100,30 +101,31 @@ async function buildEmail(purpose, email, context, { user, contents } = {}) { /** * Utility function for finding most valid SMTP configuration. * @param {object} db The CouchDB database which is to be looked up within. - * @param {string|null} workspaceId If using finer grain control of configs a workspace can be used. + * @param {string|null} groupId If using finer grain control of configs a group can be used. * @return {Promise} returns the SMTP configuration if it exists */ -async function getSmtpConfiguration(db, workspaceId = null) { +async function getSmtpConfiguration(db, groupId = null) { const params = { type: Configs.SMTP, } - if (workspaceId) { - params.workspace = workspaceId + if (groupId) { + params.group = groupId } return getScopedConfig(db, params) } /** * Checks if a SMTP config exists based on passed in parameters. + * @param groupId * @return {Promise} returns true if there is a configuration that can be used. */ -exports.isEmailConfigured = async (workspaceId = null) => { +exports.isEmailConfigured = async (groupId = null) => { // when "testing" simply return true if (TEST_MODE) { return true } - const db = getGlobalDB() - const config = await getSmtpConfiguration(db, workspaceId) + const db = new CouchDB(GLOBAL_DB) + const config = await getSmtpConfiguration(db, groupId) return config != null } @@ -132,7 +134,7 @@ exports.isEmailConfigured = async (workspaceId = null) => { * send an email using it. * @param {string} email The email address to send to. * @param {string} purpose The purpose of the email being sent (e.g. reset password). - * @param {string|undefined} workspaceId If finer grain controls being used then this will lookup config for workspace. + * @param {string|undefined} groupId If finer grain controls being used then this will lookup config for group. * @param {object|undefined} user If sending to an existing user the object can be provided, this is used in the context. * @param {string|undefined} from If sending from an address that is not what is configured in the SMTP config. * @param {string|undefined} contents If sending a custom email then can supply contents which will be added to it. @@ -144,10 +146,10 @@ exports.isEmailConfigured = async (workspaceId = null) => { exports.sendEmail = async ( email, purpose, - { workspaceId, user, from, contents, subject, info } = {} + { groupId, user, from, contents, subject, info } = {} ) => { - const db = getGlobalDB() - let config = (await getSmtpConfiguration(db, workspaceId)) || {} + const db = new CouchDB(GLOBAL_DB) + let config = (await getSmtpConfiguration(db, groupId)) || {} if (Object.keys(config).length === 0 && !TEST_MODE) { throw "Unable to find SMTP configuration." } @@ -158,10 +160,7 @@ exports.sendEmail = async ( const message = { from: from || config.from, to: email, - html: await buildEmail(purpose, email, context, { - user, - contents, - }), + html: await buildEmail(purpose, email, context, { user, contents }), } if (subject || config.subject) { message.subject = await processString(subject || config.subject, context) diff --git a/packages/worker/src/utilities/redis.js b/packages/worker/src/utilities/redis.js index 6dd4491bc4..6e55795de1 100644 --- a/packages/worker/src/utilities/redis.js +++ b/packages/worker/src/utilities/redis.js @@ -43,10 +43,8 @@ async function getACode(db, code, deleteCode = true) { } exports.init = async () => { - pwResetClient = new Client(utils.Databases.PW_RESETS) - invitationClient = new Client(utils.Databases.INVITATIONS) - await pwResetClient.init() - await invitationClient.init() + pwResetClient = await new Client(utils.Databases.PW_RESETS).init() + invitationClient = await new Client(utils.Databases.INVITATIONS).init() } /** diff --git a/packages/worker/src/utilities/templates.js b/packages/worker/src/utilities/templates.js index 4dee52b531..3ac897c10f 100644 --- a/packages/worker/src/utilities/templates.js +++ b/packages/worker/src/utilities/templates.js @@ -1,4 +1,5 @@ -const { getScopedConfig } = require("@budibase/auth/db") +const CouchDB = require("../db") +const { getScopedConfig, StaticDatabases } = require("@budibase/auth").db const { Configs, InternalTemplateBindings, @@ -7,13 +8,12 @@ const { } = require("../constants") const { checkSlashesInUrl } = require("./index") const env = require("../environment") -const { getGlobalDB, addTenantToUrl } = require("@budibase/auth/tenancy") const LOCAL_URL = `http://localhost:${env.CLUSTER_PORT || 10000}` const BASE_COMPANY = "Budibase" exports.getSettingsTemplateContext = async (purpose, code = null) => { - const db = getGlobalDB() + const db = new CouchDB(StaticDatabases.GLOBAL.name) // TODO: use more granular settings in the future if required let settings = (await getScopedConfig(db, { type: Configs.SETTINGS })) || {} if (!settings || !settings.platformUrl) { @@ -27,9 +27,7 @@ exports.getSettingsTemplateContext = async (purpose, code = null) => { [InternalTemplateBindings.COMPANY]: settings.company || BASE_COMPANY, [InternalTemplateBindings.DOCS_URL]: settings.docsUrl || "https://docs.budibase.com/", - [InternalTemplateBindings.LOGIN_URL]: checkSlashesInUrl( - addTenantToUrl(`${URL}/login`) - ), + [InternalTemplateBindings.LOGIN_URL]: checkSlashesInUrl(`${URL}/login`), [InternalTemplateBindings.CURRENT_DATE]: new Date().toISOString(), [InternalTemplateBindings.CURRENT_YEAR]: new Date().getFullYear(), } @@ -38,13 +36,13 @@ exports.getSettingsTemplateContext = async (purpose, code = null) => { case EmailTemplatePurpose.PASSWORD_RECOVERY: context[InternalTemplateBindings.RESET_CODE] = code context[InternalTemplateBindings.RESET_URL] = checkSlashesInUrl( - addTenantToUrl(`${URL}/builder/auth/reset?code=${code}`) + `${URL}/builder/auth/reset?code=${code}` ) break case EmailTemplatePurpose.INVITATION: context[InternalTemplateBindings.INVITE_CODE] = code context[InternalTemplateBindings.INVITE_URL] = checkSlashesInUrl( - addTenantToUrl(`${URL}/builder/invite?code=${code}`) + `${URL}/builder/invite?code=${code}` ) break } diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 514b850a3f..1d4227363f 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -287,66 +287,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/auth@^0.9.79-alpha.4": - version "0.9.79" - resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.79.tgz#416271ffc55e84116550469656bf151a7734a90f" - integrity sha512-ENh099tYeUfVExsAeoxwMh2ODioKQGPteK9LJiU5hMdM4Oi7pyImu287BgKpTIheB+WtadT4e21VpPaJ62APEw== - dependencies: - aws-sdk "^2.901.0" - bcryptjs "^2.4.3" - ioredis "^4.27.1" - jsonwebtoken "^8.5.1" - koa-passport "^4.1.4" - lodash "^4.17.21" - node-fetch "^2.6.1" - passport-google-auth "^1.0.2" - passport-google-oauth "^2.0.0" - passport-jwt "^4.0.0" - passport-local "^1.0.0" - sanitize-s3-objectkey "^0.0.1" - tar-fs "^2.1.1" - uuid "^8.3.2" - zlib "^1.0.5" - -"@budibase/handlebars-helpers@^0.11.4": - version "0.11.5" - resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.5.tgz#e9cc90a44e94ad536992cf10906829b633e94bc5" - integrity sha512-ZxpyNtTHxS8Y+yTicbgWvYDAydooUSjOf3Y+wmTE2d4NpDgO0g0IjepLfZV+KASv9XBr//ylJdjE4hClX9NTFw== - dependencies: - array-sort "^1.0.0" - define-property "^2.0.2" - extend-shallow "^3.0.2" - "falsey" "^1.0.0" - for-in "^1.0.2" - get-object "^0.2.0" - get-value "^3.0.1" - handlebars "^4.7.7" - handlebars-utils "^1.0.6" - has-value "^2.0.2" - helper-date "^1.0.1" - helper-markdown "^1.0.0" - helper-md "^0.2.2" - html-tag "^2.0.0" - is-even "^1.0.0" - is-glob "^4.0.1" - kind-of "^6.0.3" - micromatch "^3.1.5" - relative "^3.0.2" - striptags "^3.1.1" - to-gfm-code-block "^0.1.1" - year "^0.2.1" - -"@budibase/string-templates@^0.9.79-alpha.4": - version "0.9.79" - resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.79.tgz#bb75a7433a7cfda1fc488283f35e47879b799fcc" - integrity sha512-hkAne5mx7mj8+osXFt45VwgLKSa94uQOGOb4R8uv9WNzvk4RzcjBfRzJxggv29FUemItrAeZpSh+Um6yugFI+w== - dependencies: - "@budibase/handlebars-helpers" "^0.11.4" - dayjs "^1.10.4" - handlebars "^4.7.6" - handlebars-utils "^1.0.6" - lodash "^4.17.20" - "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -939,7 +879,7 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -argparse@^1.0.10, argparse@^1.0.7: +argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -976,15 +916,6 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -1017,13 +948,6 @@ ast-types@0.9.6: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= -async@~2.1.4: - version "2.1.5" - resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" - integrity sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw= - dependencies: - lodash "^4.14.0" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1039,13 +963,6 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -autolinker@~0.28.0: - version "0.28.1" - resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.28.1.tgz#0652b491881879f0775dace0cdca3233942a4e47" - integrity sha1-BlK0kYgYefB3XazgzcoyM5QqTkc= - dependencies: - gulp-header "^1.7.1" - aws-sdk@^2.811.0: version "2.811.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.811.0.tgz#a7e4040b2ee7d8b825b142ed5179d36dc3f315c4" @@ -1061,21 +978,6 @@ aws-sdk@^2.811.0: uuid "3.3.2" xml2js "0.4.19" -aws-sdk@^2.901.0: - version "2.953.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.953.0.tgz#2ba87da084164bcb8e325e67356b8372e54cb5d9" - integrity sha512-CCsJm+ggE1HQ2fkCto+JRqJyET81Vw8eZr/KnNw19jqRiAsXNj5GqAh1JNyTCHEif8PcjREtP7o3y093ylSUyg== - dependencies: - buffer "4.9.2" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.15.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - uuid "3.3.2" - xml2js "0.4.19" - aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -1202,15 +1104,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - boxen@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" @@ -1455,11 +1348,6 @@ chokidar@^3.2.2: optionalDependencies: fsevents "~2.3.1" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -1506,11 +1394,6 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -cluster-key-slot@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" - integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== - co-body@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/co-body/-/co-body-5.2.0.tgz#5a0a658c46029131e0e3a306f67647302f71c124" @@ -1612,13 +1495,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-with-sourcemaps@*: - version "1.1.0" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" - integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== - dependencies: - source-map "^0.6.1" - configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -1738,23 +1614,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date.js@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/date.js/-/date.js-0.3.3.tgz#ef1e92332f507a638795dbb985e951882e50bbda" - integrity sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw== - dependencies: - debug "~3.1.0" - dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dayjs@^1.10.4: - version "1.10.6" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" - integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== - debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1776,13 +1640,6 @@ debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" -debug@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -1839,13 +1696,6 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== - dependencies: - kind-of "^5.0.2" - defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -1901,11 +1751,6 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -denque@^1.1.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de" - integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ== - depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -2023,7 +1868,7 @@ encoding-down@^6.3.0: level-codec "^9.0.0" level-errors "^2.0.0" -end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -2037,11 +1882,6 @@ end-stream@~0.1.0: dependencies: write-stream "~0.4.3" -ent@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" - integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= - errno@~0.1.1: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -2283,11 +2123,6 @@ falafel@^1.0.1: isarray "0.0.1" object-keys "^1.0.6" -"falsey@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/falsey/-/falsey-1.0.0.tgz#71bdd775c24edad9f2f5c015ce8be24400bb5d7d" - integrity sha512-zMDNZ/Ipd8MY0+346CPvhzP1AsiVyNfTOayJza4reAIWf72xbkuFUDcJNxSAsQE1b9Bu0wijKb8Ngnh/a7fI5w== - fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2414,16 +2249,6 @@ fresh@~0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2463,14 +2288,6 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.1" -get-object@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/get-object/-/get-object-0.2.0.tgz#d92ff7d5190c64530cda0543dac63a3d47fe8c0c" - integrity sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw= - dependencies: - is-number "^2.0.2" - isobject "^0.2.0" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2495,13 +2312,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -get-value@^3.0.0, get-value@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-3.0.1.tgz#5efd2a157f1d6a516d7524e124ac52d0a39ef5a8" - integrity sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA== - dependencies: - isobject "^3.0.1" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2551,32 +2361,6 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -google-auth-library@~0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e" - integrity sha1-bhW6vuhf0d0U2NEoopW2g41SE24= - dependencies: - gtoken "^1.2.1" - jws "^3.1.4" - lodash.noop "^3.0.1" - request "^2.74.0" - -google-p12-pem@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177" - integrity sha1-M8RqsCGqc0+gMys5YKmj/8svMXc= - dependencies: - node-forge "^0.7.1" - -googleapis@^16.0.0: - version "16.1.0" - resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-16.1.0.tgz#0f19f2d70572d918881a0f626e3b1a2fa8629576" - integrity sha1-Dxny1wVy2RiIGg9ibjsaL6hilXY= - dependencies: - async "~2.1.4" - google-auth-library "~0.10.0" - string-template "~1.0.0" - got@^11.8.1: version "11.8.1" resolved "https://registry.yarnpkg.com/got/-/got-11.8.1.tgz#df04adfaf2e782babb3daabc79139feec2f7e85d" @@ -2626,45 +2410,6 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -gtoken@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8" - integrity sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w== - dependencies: - google-p12-pem "^0.1.0" - jws "^3.0.0" - mime "^1.4.1" - request "^2.72.0" - -gulp-header@^1.7.1: - version "1.8.12" - resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84" - integrity sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ== - dependencies: - concat-with-sourcemaps "*" - lodash.template "^4.4.0" - through2 "^2.0.0" - -handlebars-utils@^1.0.2, handlebars-utils@^1.0.4, handlebars-utils@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/handlebars-utils/-/handlebars-utils-1.0.6.tgz#cb9db43362479054782d86ffe10f47abc76357f9" - integrity sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw== - dependencies: - kind-of "^6.0.0" - typeof-article "^0.1.1" - -handlebars@^4.7.6, handlebars@^4.7.7: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -2711,14 +2456,6 @@ has-value@^1.0.0: has-values "^1.0.0" isobject "^3.0.0" -has-value@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-2.0.2.tgz#d0f12e8780ba8e90e66ad1a21c707fdb67c25658" - integrity sha512-ybKOlcRsK2MqrM3Hmz/lQxXHZ6ejzSPzpNabKB45jb5qDgJvKPa3SdapTsTLwEb9WltgWpOmNax7i+DzNOk4TA== - dependencies: - get-value "^3.0.0" - has-values "^2.0.1" - has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" @@ -2732,13 +2469,6 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has-values@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-2.0.1.tgz#3876200ff86d8a8546a9264a952c17d5fc17579d" - integrity sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w== - dependencies: - kind-of "^6.0.2" - has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -2751,39 +2481,6 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -helper-date@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/helper-date/-/helper-date-1.0.1.tgz#12fedea3ad8e44a7ca4c4efb0ff4104a5120cffb" - integrity sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w== - dependencies: - date.js "^0.3.1" - handlebars-utils "^1.0.4" - moment "^2.18.1" - -helper-markdown@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/helper-markdown/-/helper-markdown-1.0.0.tgz#ee7e9fc554675007d37eb90f7853b13ce74f3e10" - integrity sha512-AnDqMS4ejkQK0MXze7pA9TM3pu01ZY+XXsES6gEE0RmCGk5/NIfvTn0NmItfyDOjRAzyo9z6X7YHbHX4PzIvOA== - dependencies: - handlebars-utils "^1.0.2" - highlight.js "^9.12.0" - remarkable "^1.7.1" - -helper-md@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/helper-md/-/helper-md-0.2.2.tgz#c1f59d7e55bbae23362fd8a0e971607aec69d41f" - integrity sha1-wfWdflW7riM2L9ig6XFgeuxp1B8= - dependencies: - ent "^2.2.0" - extend-shallow "^2.0.1" - fs-exists-sync "^0.1.0" - remarkable "^1.6.2" - -highlight.js@^9.12.0: - version "9.18.5" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825" - integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -2801,14 +2498,6 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -html-tag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/html-tag/-/html-tag-2.0.0.tgz#36c3bc8d816fd30b570d5764a497a641640c2fed" - integrity sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g== - dependencies: - is-self-closing "^1.0.1" - kind-of "^6.0.0" - http-assert@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.4.1.tgz#c5f725d677aa7e873ef736199b89686cceb37878" @@ -2939,7 +2628,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2967,22 +2656,6 @@ inline-process-browser@^1.0.0: falafel "^1.0.1" through2 "^0.6.5" -ioredis@^4.27.1: - version "4.27.6" - resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.6.tgz#a53d427d3fe75fbd10ed7ad150ce00559df8dcf8" - integrity sha512-6W3ZHMbpCa8ByMyC1LJGOi7P2WiOKP9B3resoZOVLDhi+6dDBOW+KNsRq3yI36Hmnb2sifCxHX+YSarTeXh48A== - dependencies: - cluster-key-slot "^1.1.0" - debug "^4.3.1" - denque "^1.1.0" - lodash.defaults "^4.2.0" - lodash.flatten "^4.4.0" - p-map "^2.1.0" - redis-commands "1.7.0" - redis-errors "^1.2.0" - redis-parser "^3.0.0" - standard-as-callback "^2.1.0" - is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -3070,13 +2743,6 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-even@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-even/-/is-even-1.0.0.tgz#76b5055fbad8d294a86b6a949015e1c97b717c06" - integrity sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY= - dependencies: - is-odd "^0.1.2" - is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -3134,13 +2800,6 @@ is-npm@^4.0.0: resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== -is-number@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3158,13 +2817,6 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-odd@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-0.1.2.tgz#bc573b5ce371ef2aad6e6f49799b72bef13978a7" - integrity sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc= - dependencies: - is-number "^3.0.0" - is-path-inside@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -3182,13 +2834,6 @@ is-potential-custom-element-name@^1.0.0: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-self-closing@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-self-closing/-/is-self-closing-1.0.1.tgz#5f406b527c7b12610176320338af0fa3896416e4" - integrity sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg== - dependencies: - self-closing-tags "^1.0.1" - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3235,7 +2880,7 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -3245,11 +2890,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isobject@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-0.2.0.tgz#a3432192f39b910b5f02cc989487836ec70aa85e" - integrity sha1-o0MhkvObkQtfAsyYlIeDbscKqF4= - isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -3794,7 +3434,7 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" -jsonwebtoken@^8.2.0, jsonwebtoken@^8.5.1: +jsonwebtoken@^8.2.0: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== @@ -3849,7 +3489,7 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.0.0, jws@^3.1.4, jws@^3.2.2: +jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -3878,7 +3518,7 @@ keyv@^4.0.0: dependencies: json-buffer "3.0.1" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.1.0, kind-of@^3.2.0: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= @@ -3892,12 +3532,12 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -kind-of@^5.0.0, kind-of@^5.0.2: +kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -4158,21 +3798,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= - -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= - lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -4203,32 +3828,12 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= -lodash.noop@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" - integrity sha1-OBiPTWUKOkdCWEObluxFsyYXEzw= - lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash.template@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash@^4.14.0, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.17.19, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4308,7 +3913,7 @@ methods@^1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.1.4, micromatch@^3.1.5: +micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -4364,11 +3969,6 @@ mime-types@^2.1.18, mime-types@~2.1.24: dependencies: mime-db "1.44.0" -mime@^1.4.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - mime@^2.4.6: version "2.5.2" resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" @@ -4409,11 +4009,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp@^0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -4421,11 +4016,6 @@ mkdirp@^0.5.0: dependencies: minimist "^1.2.5" -moment@^2.18.1: - version "2.29.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== - mri@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" @@ -4478,11 +4068,6 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.6.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -4498,11 +4083,6 @@ node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-forge@^0.7.1: - version "0.7.6" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" - integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== - node-gyp-build@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" @@ -4729,11 +4309,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -4774,14 +4349,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -passport-google-auth@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938" - integrity sha1-izALWqRC70M94dgy7TESh30LKTg= - dependencies: - googleapis "^16.0.0" - passport-strategy "1.x" - passport-google-oauth1@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz#af74a803df51ec646f66a44d82282be6f108e0cc" @@ -4839,7 +4406,7 @@ passport-oauth2@1.x.x: uid2 "0.0.x" utils-merge "1.x.x" -passport-strategy@1.x, passport-strategy@1.x.x, passport-strategy@^1.0.0: +passport-strategy@1.x.x, passport-strategy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= @@ -5129,11 +4696,6 @@ private@^0.1.6, private@~0.1.5: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - prompts@^2.0.1: version "2.4.1" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" @@ -5278,7 +4840,7 @@ readable-stream@1.1.14: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -5302,19 +4864,6 @@ readable-stream@~0.0.2: resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-0.0.4.tgz#f32d76e3fb863344a548d79923007173665b3b8d" integrity sha1-8y124/uGM0SlSNeZIwBxc2ZbO40= -readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" @@ -5342,23 +4891,6 @@ recast@^0.11.17: private "~0.1.5" source-map "~0.5.0" -redis-commands@1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" - integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== - -redis-errors@^1.0.0, redis-errors@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" - integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= - -redis-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" - integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= - dependencies: - redis-errors "^1.0.0" - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -5381,21 +4913,6 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -relative@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/relative/-/relative-3.0.2.tgz#0dcd8ec54a5d35a3c15e104503d65375b5a5367f" - integrity sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8= - dependencies: - isobject "^2.0.0" - -remarkable@^1.6.2, remarkable@^1.7.1: - version "1.7.4" - resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00" - integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg== - dependencies: - argparse "^1.0.10" - autolinker "~0.28.0" - remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -5427,7 +4944,7 @@ request-promise-native@^1.0.9: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.72.0, request@^2.74.0, request@^2.88.0, request@^2.88.2: +request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -5532,7 +5049,7 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -5569,11 +5086,6 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sanitize-s3-objectkey@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e" - integrity sha512-ZTk7aqLxy4sD40GWcYWoLfbe05XLmkKvh6vGKe13ADlei24xlezcvjgKy1qRArlaIbIMYaqK7PCalvZtulZlaQ== - sax@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -5591,11 +5103,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -self-closing-tags@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/self-closing-tags/-/self-closing-tags-1.0.1.tgz#6c5fa497994bb826b484216916371accee490a5d" - integrity sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA== - semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -5871,11 +5378,6 @@ stack-utils@^2.0.2: dependencies: escape-string-regexp "^2.0.0" -standard-as-callback@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" - integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -5912,11 +5414,6 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-template@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" - integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y= - string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -5947,13 +5444,6 @@ string_decoder@~0.10.x: resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - strip-ansi@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -5993,11 +5483,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -striptags@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.2.0.tgz#cc74a137db2de8b0b9a370006334161f7dd67052" - integrity sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw== - sublevel-pouchdb@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/sublevel-pouchdb/-/sublevel-pouchdb-7.2.2.tgz#49e46cd37883bf7ff5006d7c5b9bcc7bcc1f422f" @@ -6060,27 +5545,6 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -tar-fs@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - term-size@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" @@ -6124,14 +5588,6 @@ through2@^0.6.2, through2@^0.6.5: readable-stream ">=1.0.33-1 <1.1.0-0" xtend ">=4.0.0 <4.1.0-0" -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -6152,11 +5608,6 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= -to-gfm-code-block@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz#25d045a5fae553189e9637b590900da732d8aa82" - integrity sha1-JdBFpfrlUxielje1kJANpzLYqoI= - to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" @@ -6289,18 +5740,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typeof-article@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/typeof-article/-/typeof-article-0.1.1.tgz#9f07e733c3fbb646ffa9e61c08debacd460e06af" - integrity sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8= - dependencies: - kind-of "^3.1.0" - -uglify-js@^3.1.4: - version "3.14.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.0.tgz#2d723a0afee81e0d08db9354a9c277006e942386" - integrity sha512-R/tiGB1ZXp2BC+TkRGLwj8xUZgdfT2f4UZEgX6aVjJ5uttPrr4fYmwTWDGqVnBCLbOXRMY6nr/BTbwCtVfps0g== - uid2@0.0.x: version "0.0.3" resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" @@ -6357,11 +5796,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -update-dotenv@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/update-dotenv/-/update-dotenv-1.1.1.tgz#17146f302f216c3c92419d5a327a45be910050ca" - integrity sha512-3cIC18In/t0X/yH793c00qqxcKD8jVCgNOPif/fGQkFpYMGecM9YAc+kaAKXuZsM2dE9I9wFI7KvAuNX22SGMQ== - update-notifier@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" @@ -6418,7 +5852,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -6443,7 +5877,7 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0, uuid@^8.3.2: +uuid@^8.3.0: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -6575,11 +6009,6 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -6651,7 +6080,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.2, xtend@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -6691,17 +6120,7 @@ yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -year@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/year/-/year-0.2.1.tgz#4083ae520a318b23ec86037f3000cb892bdf9bb0" - integrity sha1-QIOuUgoxiyPshgN/MADLiSvfm7A= - ylru@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ== - -zlib@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0" - integrity sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA=