From 0dd46d12fac5eedcae13617a2664e85549f3a5c4 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 23 Apr 2021 18:07:39 +0100 Subject: [PATCH] Updating test cases and some re-work of the email system. --- packages/auth/src/index.js | 2 +- packages/auth/src/middleware/authenticated.js | 21 ++++-- packages/auth/src/utils.js | 2 +- .../server/src/api/routes/tests/query.spec.js | 2 +- .../src/api/routes/tests/routing.spec.js | 12 +++- .../server/src/api/routes/tests/user.spec.js | 4 +- .../routes/tests/utilities/TestFunctions.js | 15 +++-- packages/server/src/middleware/currentapp.js | 8 +-- .../src/middleware/tests/currentapp.spec.js | 8 +-- .../src/tests/utilities/TestConfiguration.js | 66 ++++++++++++++----- .../worker/src/api/controllers/admin/email.js | 6 +- packages/worker/src/api/index.js | 7 ++ .../worker/src/api/routes/admin/templates.js | 2 + packages/worker/src/api/routes/index.js | 12 +++- .../tests/utilities/TestConfiguration.js | 16 +++-- .../worker/src/constants/templates/index.js | 10 ++- packages/worker/src/utilities/email.js | 2 +- packages/worker/src/utilities/templates.js | 2 +- 18 files changed, 139 insertions(+), 58 deletions(-) diff --git a/packages/auth/src/index.js b/packages/auth/src/index.js index 5be91cc102..348f911f80 100644 --- a/packages/auth/src/index.js +++ b/packages/auth/src/index.js @@ -36,7 +36,7 @@ module.exports = { buildAuthMiddleware: authenticated, passport, google, - jwt, + jwt: require("jsonwebtoken"), }, StaticDatabases, constants: require("./constants"), diff --git a/packages/auth/src/middleware/authenticated.js b/packages/auth/src/middleware/authenticated.js index 443384ee76..d64c30a70a 100644 --- a/packages/auth/src/middleware/authenticated.js +++ b/packages/auth/src/middleware/authenticated.js @@ -1,6 +1,6 @@ const { Cookies } = require("../constants") const database = require("../db") -const { getCookie } = require("../utils") +const { getCookie, clearCookie } = require("../utils") const { StaticDatabases } = require("../db/utils") module.exports = (noAuthPatterns = []) => { @@ -15,11 +15,20 @@ module.exports = (noAuthPatterns = []) => { const authCookie = getCookie(ctx, Cookies.Auth) if (authCookie) { - const db = database.getDB(StaticDatabases.GLOBAL.name) - const user = await db.get(authCookie.userId) - delete user.password - ctx.isAuthenticated = true - ctx.user = user + try { + const db = database.getDB(StaticDatabases.GLOBAL.name) + const user = await db.get(authCookie.userId) + delete user.password + ctx.isAuthenticated = true + ctx.user = user + } catch (err) { + // remove the cookie as the use does not exist anymore + clearCookie(ctx, Cookies.Auth) + } + } + // be explicit + if (ctx.isAuthenticated !== true) { + ctx.isAuthenticated = false } return next() diff --git a/packages/auth/src/utils.js b/packages/auth/src/utils.js index b9514d059c..10507410b1 100644 --- a/packages/auth/src/utils.js +++ b/packages/auth/src/utils.js @@ -92,7 +92,7 @@ exports.setCookie = (ctx, value, name = "builder") => { * Utility function, simply calls setCookie with an empty string for value */ exports.clearCookie = (ctx, name) => { - exports.setCookie(ctx, "", name) + exports.setCookie(ctx, null, name) } /** diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 2755c0230e..eadd475ed4 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -93,7 +93,7 @@ describe("/queries", () => { const query = await config.createQuery() const res = await request .get(`/api/queries/${query._id}`) - .set(await config.roleHeaders()) + .set(await config.roleHeaders({})) .expect("Content-Type", /json/) .expect(200) expect(res.body.fields).toBeUndefined() diff --git a/packages/server/src/api/routes/tests/routing.spec.js b/packages/server/src/api/routes/tests/routing.spec.js index 5fe0e26f15..d40b35ba8d 100644 --- a/packages/server/src/api/routes/tests/routing.spec.js +++ b/packages/server/src/api/routes/tests/routing.spec.js @@ -35,7 +35,11 @@ describe("/routing", () => { }) const res = await request .get(`/api/routing/client`) - .set(await config.roleHeaders("basic@test.com", BUILTIN_ROLE_IDS.BASIC)) + .set(await config.roleHeaders({ + email: "basic@test.com", + roleId: BUILTIN_ROLE_IDS.BASIC, + builder: false + })) .expect("Content-Type", /json/) .expect(200) expect(res.body.routes).toBeDefined() @@ -59,7 +63,11 @@ describe("/routing", () => { }) const res = await request .get(`/api/routing/client`) - .set(await config.roleHeaders("basic@test.com", BUILTIN_ROLE_IDS.POWER)) + .set(await config.roleHeaders({ + email: "basic@test.com", + roleId: BUILTIN_ROLE_IDS.POWER, + builder: false, + })) .expect("Content-Type", /json/) .expect(200) expect(res.body.routes).toBeDefined() diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index e80672284d..b474e360a8 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -5,7 +5,9 @@ const { basicUser } = setup.structures const workerRequests = require("../../../utilities/workerRequests") jest.mock("../../../utilities/workerRequests", () => ({ - getGlobalUsers: jest.fn(), + getGlobalUsers: jest.fn(() => { + return {} + }), saveGlobalUser: jest.fn(() => { const uuid = require("uuid/v4") return { diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.js b/packages/server/src/api/routes/tests/utilities/TestFunctions.js index 9ee68c283f..0e11dd2056 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.js +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.js @@ -46,7 +46,10 @@ exports.createRequest = (request, method, url, body) => { } exports.checkBuilderEndpoint = async ({ config, method, url, body }) => { - const headers = await config.login() + const headers = await config.login("test@test.com", "test", { + userId: "us_fail", + builder: false, + }) await exports .createRequest(config.request, method, url, body) .set(headers) @@ -62,9 +65,10 @@ exports.checkPermissionsEndpoint = async ({ failRole, }) => { const password = "PASSWORD" - await config.createUser("passUser@budibase.com", password, passRole) - const passHeader = await config.login("passUser@budibase.com", password, { + let user = await config.createUser("pass@budibase.com", password, passRole) + const passHeader = await config.login("pass@budibase.com", password, { roleId: passRole, + userId: user.globalId, }) await exports @@ -72,9 +76,10 @@ exports.checkPermissionsEndpoint = async ({ .set(passHeader) .expect(200) - await config.createUser("failUser@budibase.com", password, failRole) - const failHeader = await config.login("failUser@budibase.com", password, { + user = await config.createUser("fail@budibase.com", password, failRole) + const failHeader = await config.login("fail@budibase.com", password, { roleId: failRole, + userId: user.globalId, }) await exports diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index d85d2158c2..0828c809c7 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -3,10 +3,7 @@ const { Cookies } = require("@budibase/auth").constants const { getRole } = require("../utilities/security/roles") const { getGlobalUsers } = require("../utilities/workerRequests") const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") -const { - getGlobalIDFromUserMetadataID, - generateUserMetadataID, -} = require("../db/utils") +const { generateUserMetadataID } = require("../db/utils") module.exports = async (ctx, next) => { // try to get the appID from the request @@ -31,8 +28,7 @@ module.exports = async (ctx, next) => { appCookie.roleId === BUILTIN_ROLE_IDS.PUBLIC) ) { // Different App ID means cookie needs reset, or if the same public user has logged in - const globalId = getGlobalIDFromUserMetadataID(ctx.user._id) - const globalUser = await getGlobalUsers(ctx, requestAppId, globalId) + const globalUser = await getGlobalUsers(ctx, requestAppId, ctx.user._id) updateCookie = true appId = requestAppId if (globalUser.roles && globalUser.roles[requestAppId]) { diff --git a/packages/server/src/middleware/tests/currentapp.spec.js b/packages/server/src/middleware/tests/currentapp.spec.js index 61d5bf018d..e83b364cb5 100644 --- a/packages/server/src/middleware/tests/currentapp.spec.js +++ b/packages/server/src/middleware/tests/currentapp.spec.js @@ -5,7 +5,7 @@ function mockWorker() { jest.mock("../../utilities/workerRequests", () => ({ getGlobalUsers: () => { return { - email: "us_uuid1", + _id: "us_uuid1", roles: { "app_test": "BASIC", } @@ -67,7 +67,8 @@ class TestConfiguration { setUser() { this.ctx.user = { - userId: "ro_ta_user_us_uuid1", + userId: "us_uuid1", + _id: "us_uuid1", } } @@ -159,5 +160,4 @@ describe("Current app middleware", () => { await checkExpected(false) }) }) - -}) \ No newline at end of file +}) diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 0b19a74b90..9b4328ba3a 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -16,7 +16,10 @@ const supertest = require("supertest") const { cleanup } = require("../../utilities/fileSystem") const { Cookies } = require("@budibase/auth").constants const { jwt } = require("@budibase/auth").auth +const { StaticDatabases } = require("@budibase/auth").db +const CouchDB = require("../../db") +const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" const PASSWORD = "babs_password" @@ -57,7 +60,27 @@ class TestConfiguration { return request.body } + async globalUser(id = GLOBAL_USER_ID, builder = true) { + const db = new CouchDB(StaticDatabases.GLOBAL.name) + let existing + try { + existing = await db.get(id) + } catch (err) { + existing = {} + } + const user = { + _id: id, + ...existing, + roles: {}, + } + if (builder) { + user.builder = { global: true } + } + await db.put(user) + } + async init(appName = "test_application") { + await this.globalUser() return this.createApp(appName) } @@ -69,17 +92,14 @@ class TestConfiguration { } defaultHeaders() { - const user = { - userId: "ro_ta_user_us_uuid1", - builder: { - global: true, - }, + const auth = { + userId: GLOBAL_USER_ID, } const app = { roleId: BUILTIN_ROLE_IDS.BUILDER, appId: this.appId, } - const authToken = jwt.sign(user, env.JWT_SECRET) + const authToken = jwt.sign(auth, env.JWT_SECRET) const appToken = jwt.sign(app, env.JWT_SECRET) const headers = { Accept: "application/json", @@ -104,14 +124,18 @@ class TestConfiguration { return headers } - async roleHeaders(email = EMAIL, roleId = BUILTIN_ROLE_IDS.ADMIN) { + async roleHeaders({ + email = EMAIL, + roleId = BUILTIN_ROLE_IDS.ADMIN, + builder = false, + }) { let user try { user = await this.createUser(email, PASSWORD, roleId) } catch (err) { // allow errors here } - return this.login(email, PASSWORD, { roleId, userId: user._id }) + return this.login(email, PASSWORD, { roleId, userId: user._id, builder }) } async createApp(appName) { @@ -282,7 +306,9 @@ class TestConfiguration { password = PASSWORD, roleId = BUILTIN_ROLE_IDS.POWER ) { - return this._req( + const globalId = `us_${Math.random()}` + await this.globalUser(globalId, roleId === BUILTIN_ROLE_IDS.BUILDER) + const user = await this._req( { email, password, @@ -291,28 +317,34 @@ class TestConfiguration { null, controllers.user.createMetadata ) + return { + ...user, + globalId, + } } - async login(email, password, { roleId, userId } = {}) { - if (!roleId) { - roleId = BUILTIN_ROLE_IDS.BUILDER - } + async login(email, password, { roleId, userId, builder } = {}) { + roleId = !roleId ? BUILTIN_ROLE_IDS.BUILDER : roleId + userId = !userId ? `us_uuid1` : userId if (!this.request) { throw "Server has not been opened, cannot login." } + // make sure the user exists in the global DB + if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) { + await this.globalUser(userId, builder) + } if (!email || !password) { await this.createUser() } // have to fake this - const user = { - userId: userId || `us_uuid1`, - email: email || EMAIL, + const auth = { + userId, } const app = { roleId: roleId, appId: this.appId, } - const authToken = jwt.sign(user, env.JWT_SECRET) + const authToken = jwt.sign(auth, env.JWT_SECRET) const appToken = jwt.sign(app, env.JWT_SECRET) // returning necessary request headers diff --git a/packages/worker/src/api/controllers/admin/email.js b/packages/worker/src/api/controllers/admin/email.js index 0755596841..d735023f7c 100644 --- a/packages/worker/src/api/controllers/admin/email.js +++ b/packages/worker/src/api/controllers/admin/email.js @@ -66,5 +66,9 @@ exports.sendEmail = async ctx => { to: email, html: await buildEmail(purpose, email, user), } - await transport.sendMail(message) + const response = await transport.sendMail(message) + ctx.body = { + ...response, + message: `Email sent to ${email}.`, + } } diff --git a/packages/worker/src/api/index.js b/packages/worker/src/api/index.js index bc3c74aac9..df346c8e0e 100644 --- a/packages/worker/src/api/index.js +++ b/packages/worker/src/api/index.js @@ -23,6 +23,13 @@ router ) .use("/health", ctx => (ctx.status = 200)) .use(buildAuthMiddleware(NO_AUTH_ENDPOINTS)) + // for now no public access is allowed to worker (bar health check) + .use((ctx, next) => { + if (!ctx.isAuthenticated) { + ctx.throw(403, "Unauthorized - no public worker access") + } + return next() + }) // error handling middleware router.use(async (ctx, next) => { diff --git a/packages/worker/src/api/routes/admin/templates.js b/packages/worker/src/api/routes/admin/templates.js index 81bd814cbc..a00278ccac 100644 --- a/packages/worker/src/api/routes/admin/templates.js +++ b/packages/worker/src/api/routes/admin/templates.js @@ -27,3 +27,5 @@ router .get("/api/admin/template/:ownerId", controller.fetchByOwner) .get("/api/admin/template/:id", controller.find) .delete("/api/admin/template/:id/:rev", controller.destroy) + +module.exports = router diff --git a/packages/worker/src/api/routes/index.js b/packages/worker/src/api/routes/index.js index aa1c6874e3..419c80e505 100644 --- a/packages/worker/src/api/routes/index.js +++ b/packages/worker/src/api/routes/index.js @@ -1,7 +1,17 @@ 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("./auth") const appRoutes = require("./app") -exports.routes = [configRoutes, userRoutes, groupRoutes, authRoutes, appRoutes] +exports.routes = [ + configRoutes, + userRoutes, + groupRoutes, + authRoutes, + appRoutes, + templateRoutes, + emailRoutes +] diff --git a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js index 18656a0a2a..6e499642d7 100644 --- a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js @@ -36,12 +36,20 @@ class TestConfiguration { return request.body } - defaultHeaders() { - const user = { - userId: "us_uuid1", + async init() { + // create a test user + await this._req({ + _id: "us_uuid1", builder: { global: true, - }, + } + }, null, controllers.users.save) + } + + defaultHeaders() { + const user = { + _id: "us_uuid1", + userId: "us_uuid1", } const authToken = jwt.sign(user, env.JWT_SECRET) return { diff --git a/packages/worker/src/constants/templates/index.js b/packages/worker/src/constants/templates/index.js index 16ec84a379..1333baa39a 100644 --- a/packages/worker/src/constants/templates/index.js +++ b/packages/worker/src/constants/templates/index.js @@ -8,18 +8,16 @@ const { join } = require("path") const CouchDB = require("../../db") const { getTemplateParams, StaticDatabases } = require("@budibase/auth").db -const TEMPLATE_PATH = join(__dirname, "..", "constants", "templates") - exports.EmailTemplates = { [EmailTemplatePurpose.PASSWORD_RECOVERY]: readStaticFile( - join(TEMPLATE_PATH, "passwordRecovery.html") + join(__dirname, "passwordRecovery.html") ), [EmailTemplatePurpose.INVITATION]: readStaticFile( - join(TEMPLATE_PATH, "invitation.html") + join(__dirname, "invitation.html") ), - [EmailTemplatePurpose.BASE]: readStaticFile(join(TEMPLATE_PATH, "base.html")), + [EmailTemplatePurpose.BASE]: readStaticFile(join(__dirname, "base.html")), [EmailTemplatePurpose.STYLES]: readStaticFile( - join(TEMPLATE_PATH, "style.css") + join(__dirname, "style.css") ), } diff --git a/packages/worker/src/utilities/email.js b/packages/worker/src/utilities/email.js index bc3933531a..b5e64a2793 100644 --- a/packages/worker/src/utilities/email.js +++ b/packages/worker/src/utilities/email.js @@ -1,6 +1,6 @@ const nodemailer = require("nodemailer") -exports.createSMTPTransport = (config) => { +exports.createSMTPTransport = config => { const options = { port: config.port, host: config.host, diff --git a/packages/worker/src/utilities/templates.js b/packages/worker/src/utilities/templates.js index 9a3e2d291b..22a9268366 100644 --- a/packages/worker/src/utilities/templates.js +++ b/packages/worker/src/utilities/templates.js @@ -1,4 +1,4 @@ -const CouchDB = require("../../../db") +const CouchDB = require("../db") const { getConfigParams, StaticDatabases } = require("@budibase/auth").db const { Configs, TemplateBindings, LOGO_URL } = require("../constants") const { checkSlashesInUrl } = require("./index")