Some re-work of the auth package, making it a bit easier to use/less likely to make a mistake.

This commit is contained in:
mike12345567 2021-04-21 16:42:44 +01:00
parent 89fef34401
commit 76ceb6a951
21 changed files with 164 additions and 106 deletions

View File

@ -14,6 +14,7 @@ const DocumentTypes = {
USER: "us", USER: "us",
APP: "app", APP: "app",
GROUP: "group", GROUP: "group",
TEMPLATE: "template",
} }
exports.DocumentTypes = DocumentTypes exports.DocumentTypes = DocumentTypes
@ -53,7 +54,7 @@ exports.generateGlobalUserID = () => {
/** /**
* Gets parameters for retrieving users. * Gets parameters for retrieving users.
*/ */
exports.getGlobalUserParams = (globalId = "", otherProps = {}) => { exports.getGlobalUserParams = (globalId, otherProps = {}) => {
if (!globalId) { if (!globalId) {
globalId = "" globalId = ""
} }
@ -63,3 +64,27 @@ exports.getGlobalUserParams = (globalId = "", otherProps = {}) => {
endkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`, endkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`,
} }
} }
/**
* Generates a template ID.
* @param ownerId The owner/user of the template, this could be global or a group level.
*/
exports.generateTemplateID = ownerId => {
return `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${newid()}`
}
/**
* Gets parameters for retrieving templates. Owner ID must be specified, either global or a group level.
*/
exports.getTemplateParams = (ownerId, templateId, otherProps = {}) => {
if (!templateId) {
templateId = ""
}
const base = `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}`
const final = templateId ? `${base}${SEPARATOR}${templateId}` : base
return {
...otherProps,
startkey: final,
endkey: `${final}${UNICODE_MAX}`,
}
}

View File

@ -5,23 +5,6 @@ const JwtStrategy = require("passport-jwt").Strategy
const { setDB, getDB } = require("./db") const { setDB, getDB } = require("./db")
const { StaticDatabases } = require("./db/utils") const { StaticDatabases } = require("./db/utils")
const { jwt, local, authenticated } = require("./middleware") const { jwt, local, authenticated } = require("./middleware")
const { Cookies, UserStatus } = require("./constants")
const { hash, compare } = require("./hashing")
const {
getAppId,
setCookie,
getCookie,
clearCookie,
isClient,
getGlobalUserByEmail,
} = require("./utils")
const {
generateGlobalUserID,
getGlobalUserParams,
generateGroupID,
getGroupParams,
} = require("./db/utils")
// Strategies // Strategies
passport.use(new LocalStrategy(local.options, local.authenticate)) passport.use(new LocalStrategy(local.options, local.authenticate))
passport.use(new JwtStrategy(jwt.options, jwt.authenticate)) passport.use(new JwtStrategy(jwt.options, jwt.authenticate))
@ -45,21 +28,14 @@ module.exports = {
init(pouch) { init(pouch) {
setDB(pouch) setDB(pouch)
}, },
db: require("./db/utils"),
utils: {
...require("./utils"),
...require("./hashing")
},
auth: {
buildAuthMiddleware: authenticated,
passport, passport,
Cookies, },
UserStatus, constants: require("./constants"),
StaticDatabases,
generateGlobalUserID,
getGlobalUserParams,
generateGroupID,
getGroupParams,
hash,
compare,
getAppId,
setCookie,
getCookie,
clearCookie,
authenticated,
isClient,
getGlobalUserByEmail,
} }

View File

@ -1,7 +1,13 @@
const { Cookies } = require("../constants") const { Cookies } = require("../constants")
const { getCookie } = require("../utils") const { getCookie } = require("../utils")
module.exports = async (ctx, next) => { module.exports = (noAuthPatterns = []) => {
const regex = new RegExp(noAuthPatterns.join("|"))
return async (ctx, next) => {
// the path is not authenticated
if (regex.test(ctx.request.url)) {
return next()
}
try { try {
// check the actual user is authenticated first // check the actual user is authenticated first
const authCookie = getCookie(ctx, Cookies.Auth) const authCookie = getCookie(ctx, Cookies.Auth)
@ -11,8 +17,9 @@ module.exports = async (ctx, next) => {
ctx.user = authCookie ctx.user = authCookie
} }
await next() return next()
} catch (err) { } catch (err) {
ctx.throw(err.status || 403, err) ctx.throw(err.status || 403, err)
} }
} }
}

View File

@ -1,14 +1,21 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const { authenticated } = require("@budibase/auth") const { buildAuthMiddleware } = require("@budibase/auth").auth
const currentApp = require("../middleware/currentapp") const currentApp = require("../middleware/currentapp")
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
const { mainRoutes, authRoutes, staticRoutes } = require("./routes") const { mainRoutes, staticRoutes } = require("./routes")
const pkg = require("../../package.json") const pkg = require("../../package.json")
const router = new Router() const router = new Router()
const env = require("../environment") const env = require("../environment")
const NO_AUTH_ENDPOINTS = [
"/health",
"/version",
"webhooks/trigger",
"webhooks/schema",
]
router router
.use( .use(
compress({ compress({
@ -31,7 +38,7 @@ router
}) })
.use("/health", ctx => (ctx.status = 200)) .use("/health", ctx => (ctx.status = 200))
.use("/version", ctx => (ctx.body = pkg.version)) .use("/version", ctx => (ctx.body = pkg.version))
.use(authenticated) .use(buildAuthMiddleware(NO_AUTH_ENDPOINTS))
.use(currentApp) .use(currentApp)
// error handling middleware // error handling middleware
@ -53,9 +60,6 @@ router.use(async (ctx, next) => {
router.get("/health", ctx => (ctx.status = 200)) router.get("/health", ctx => (ctx.status = 200))
router.use(authRoutes.routes())
router.use(authRoutes.allowedMethods())
// authenticated routes // authenticated routes
for (let route of mainRoutes) { for (let route of mainRoutes) {
router.use(route.routes()) router.use(route.routes())

View File

@ -25,6 +25,7 @@ const backupRoutes = require("./backup")
const devRoutes = require("./dev") const devRoutes = require("./dev")
exports.mainRoutes = [ exports.mainRoutes = [
authRoutes,
deployRoutes, deployRoutes,
layoutRoutes, layoutRoutes,
screenRoutes, screenRoutes,
@ -52,5 +53,4 @@ exports.mainRoutes = [
rowRoutes, rowRoutes,
] ]
exports.authRoutes = authRoutes
exports.staticRoutes = staticRoutes exports.staticRoutes = staticRoutes

View File

@ -1,5 +1,5 @@
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
const { UserStatus } = require("@budibase/auth") const { UserStatus } = require("@budibase/auth").constants
exports.LOGO_URL = exports.LOGO_URL =
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg" "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg"

View File

@ -1,4 +1,5 @@
const { getAppId, setCookie, getCookie, Cookies } = require("@budibase/auth") const { getAppId, setCookie, getCookie } = require("@budibase/auth").utils
const { Cookies } = require("@budibase/auth").constants
const { getRole } = require("../utilities/security/roles") const { getRole } = require("../utilities/security/roles")
const { getGlobalUsers } = require("../utilities/workerRequests") const { getGlobalUsers } = require("../utilities/workerRequests")
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")

View File

@ -23,10 +23,14 @@ function mockAuthWithNoCookie() {
jest.resetModules() jest.resetModules()
mockWorker() mockWorker()
jest.mock("@budibase/auth", () => ({ jest.mock("@budibase/auth", () => ({
utils: {
getAppId: jest.fn(), getAppId: jest.fn(),
setCookie: jest.fn(), setCookie: jest.fn(),
getCookie: jest.fn(), getCookie: jest.fn(),
},
constants: {
Cookies: {}, Cookies: {},
},
})) }))
} }
@ -34,15 +38,19 @@ function mockAuthWithCookie() {
jest.resetModules() jest.resetModules()
mockWorker() mockWorker()
jest.mock("@budibase/auth", () => ({ jest.mock("@budibase/auth", () => ({
utils: {
getAppId: () => { getAppId: () => {
return "app_test" return "app_test"
}, },
setCookie: jest.fn(), setCookie: jest.fn(),
getCookie: () => ({appId: "app_different", roleId: "PUBLIC"}), getCookie: () => ({appId: "app_different", roleId: "PUBLIC"}),
},
constants: {
Cookies: { Cookies: {
Auth: "auth", Auth: "auth",
CurrentApp: "currentapp", CurrentApp: "currentapp",
} },
},
})) }))
} }
@ -102,7 +110,7 @@ describe("Current app middleware", () => {
async function checkExpected(setCookie) { async function checkExpected(setCookie) {
config.setUser() config.setUser()
await config.executeMiddleware() await config.executeMiddleware()
const cookieFn = require("@budibase/auth").setCookie const cookieFn = require("@budibase/auth").utils.setCookie
if (setCookie) { if (setCookie) {
expect(cookieFn).toHaveBeenCalled() expect(cookieFn).toHaveBeenCalled()
} else { } else {
@ -122,12 +130,16 @@ describe("Current app middleware", () => {
it("should perform correct when no cookie exists", async () => { it("should perform correct when no cookie exists", async () => {
mockReset() mockReset()
jest.mock("@budibase/auth", () => ({ jest.mock("@budibase/auth", () => ({
utils: {
getAppId: () => { getAppId: () => {
return "app_test" return "app_test"
}, },
setCookie: jest.fn(), setCookie: jest.fn(),
getCookie: jest.fn(), getCookie: jest.fn(),
},
constants: {
Cookies: {}, Cookies: {},
},
})) }))
await checkExpected(true) await checkExpected(true)
}) })
@ -135,12 +147,14 @@ describe("Current app middleware", () => {
it("lastly check what occurs when cookie doesn't need updated", async () => { it("lastly check what occurs when cookie doesn't need updated", async () => {
mockReset() mockReset()
jest.mock("@budibase/auth", () => ({ jest.mock("@budibase/auth", () => ({
utils: {
getAppId: () => { getAppId: () => {
return "app_test" return "app_test"
}, },
setCookie: jest.fn(), setCookie: jest.fn(),
getCookie: () => ({appId: "app_test", roleId: "BASIC"}), getCookie: () => ({appId: "app_test", roleId: "BASIC"}),
Cookies: {}, },
constants: { Cookies: {} },
})) }))
await checkExpected(false) await checkExpected(false)
}) })

View File

@ -15,7 +15,7 @@ const {
const controllers = require("./controllers") const controllers = require("./controllers")
const supertest = require("supertest") const supertest = require("supertest")
const { cleanup } = require("../../utilities/fileSystem") const { cleanup } = require("../../utilities/fileSystem")
const { Cookies } = require("@budibase/auth") const { Cookies } = require("@budibase/auth").constants
const EMAIL = "babs@babs.com" const EMAIL = "babs@babs.com"
const PASSWORD = "babs_password" const PASSWORD = "babs_password"

View File

@ -1,6 +1,5 @@
const CouchDB = require("../../../db") const CouchDB = require("../../../db")
const { getGroupParams, StaticDatabases } = require("@budibase/auth") const { getGroupParams, generateGroupID, StaticDatabases } = require("@budibase/auth").db
const { generateGroupID } = require("@budibase/auth")
const GLOBAL_DB = StaticDatabases.GLOBAL.name const GLOBAL_DB = StaticDatabases.GLOBAL.name

View File

@ -1,7 +0,0 @@
const users = require("./users")
const groups = require("./groups")
module.exports = {
users,
groups,
}

View File

@ -0,0 +1,18 @@
const { generateTemplateID, getTemplateParams } = require("@budibase/auth").db
const { CouchDB } = require("../../../db")
exports.save = async ctx => {
}
exports.fetch = async ctx => {
}
exports.find = async ctx => {
}
exports.destroy = async ctx => {
}

View File

@ -1,11 +1,10 @@
const CouchDB = require("../../../db") const CouchDB = require("../../../db")
const { const {
hash,
generateGlobalUserID, generateGlobalUserID,
getGlobalUserParams, getGlobalUserParams,
StaticDatabases, StaticDatabases,
getGlobalUserByEmail, } = require("@budibase/auth").db
} = require("@budibase/auth") const { hash, getGlobalUserByEmail } = require("@budibase/auth").utils
const { UserStatus } = require("../../../constants") const { UserStatus } = require("../../../constants")
const FIRST_USER_EMAIL = "test@test.com" const FIRST_USER_EMAIL = "test@test.com"

View File

@ -1,4 +1,7 @@
const { passport, Cookies, clearCookie } = require("@budibase/auth") const authPkg = require("@budibase/auth")
const { clearCookie } = authPkg.utils
const { Cookies } = authPkg.constants
const { passport } = authPkg.auth
exports.authenticate = async (ctx, next) => { exports.authenticate = async (ctx, next) => {
return passport.authenticate("local", async (err, user) => { return passport.authenticate("local", async (err, user) => {

View File

@ -2,6 +2,9 @@ const Router = require("@koa/router")
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
const { routes } = require("./routes") const { routes } = require("./routes")
const { buildAuthMiddleware } = require("@budibase/auth").auth
const NO_AUTH_ENDPOINTS = ["/api/admin/users/first"]
const router = new Router() const router = new Router()
@ -19,6 +22,7 @@ router
}) })
) )
.use("/health", ctx => (ctx.status = 200)) .use("/health", ctx => (ctx.status = 200))
.use(buildAuthMiddleware(NO_AUTH_ENDPOINTS))
// error handling middleware // error handling middleware
router.use(async (ctx, next) => { router.use(async (ctx, next) => {

View File

@ -1,7 +1,6 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../../controllers/admin/groups") const controller = require("../../controllers/admin/groups")
const joiValidator = require("../../../middleware/joi-validator") const joiValidator = require("../../../middleware/joi-validator")
const { authenticated } = require("@budibase/auth")
const Joi = require("joi") const Joi = require("joi")
const router = Router() const router = Router()
@ -28,11 +27,10 @@ router
.post( .post(
"/api/admin/groups", "/api/admin/groups",
buildGroupSaveValidation(), buildGroupSaveValidation(),
authenticated,
controller.save controller.save
) )
.delete("/api/admin/groups/:id", authenticated, controller.destroy) .get("/api/admin/groups", controller.fetch)
.get("/api/admin/groups", authenticated, controller.fetch) .delete("/api/admin/groups/:id", controller.destroy)
.get("/api/admin/groups/:id", authenticated, controller.find) .get("/api/admin/groups/:id", controller.find)
module.exports = router module.exports = router

View File

@ -0,0 +1,20 @@
const Router = require("@koa/router")
const controller = require("../../controllers/admin/templates")
const joiValidator = require("../../../middleware/joi-validator")
const Joi = require("joi")
const router = Router()
function buildTemplateSaveValidation() {
}
router
.post(
"/api/admin/template/:type",
buildTemplateSaveValidation(),
controller.save
)
.get("/api/admin/template/:type", controller.fetch)
.delete("/api/admin/template/:type/:id", controller.destroy)
.get("/api/admin/template/:type/:id", controller.find)

View File

@ -1,7 +1,6 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../../controllers/admin/users") const controller = require("../../controllers/admin/users")
const joiValidator = require("../../../middleware/joi-validator") const joiValidator = require("../../../middleware/joi-validator")
const { authenticated } = require("@budibase/auth")
const Joi = require("joi") const Joi = require("joi")
const router = Router() const router = Router()
@ -29,12 +28,11 @@ router
.post( .post(
"/api/admin/users", "/api/admin/users",
buildUserSaveValidation(), buildUserSaveValidation(),
authenticated,
controller.userSave controller.userSave
) )
.get("/api/admin/users", controller.userFetch)
.post("/api/admin/users/first", controller.firstUser) .post("/api/admin/users/first", controller.firstUser)
.delete("/api/admin/users/:id", authenticated, controller.userDelete) .delete("/api/admin/users/:id", controller.userDelete)
.get("/api/admin/users", authenticated, controller.userFetch) .get("/api/admin/users/:id", controller.userFind)
.get("/api/admin/users/:id", authenticated, controller.userFind)
module.exports = router module.exports = router

View File

@ -1,9 +1,8 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const controller = require("../controllers/app") const controller = require("../controllers/app")
const { authenticated } = require("@budibase/auth")
const router = Router() const router = Router()
router.get("/api/apps", authenticated, controller.getApps) router.get("/api/apps", controller.getApps)
module.exports = router module.exports = router

View File

@ -1,5 +1,5 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const { passport } = require("@budibase/auth") const { passport } = require("@budibase/auth").auth
const authController = require("../controllers/auth") const authController = require("../controllers/auth")
const router = Router() const router = Router()

View File

@ -5,7 +5,7 @@ require("@budibase/auth").init(CouchDB)
const Koa = require("koa") const Koa = require("koa")
const destroyable = require("server-destroy") const destroyable = require("server-destroy")
const koaBody = require("koa-body") const koaBody = require("koa-body")
const { passport } = require("@budibase/auth") const { passport } = require("@budibase/auth").auth
const logger = require("koa-pino-logger") const logger = require("koa-pino-logger")
const http = require("http") const http = require("http")
const api = require("./api") const api = require("./api")