config specificity
This commit is contained in:
parent
1c7689e694
commit
737e9dba47
|
@ -98,20 +98,21 @@ exports.getTemplateParams = (ownerId, templateId, otherProps = {}) => {
|
|||
* Generates a new configuration ID.
|
||||
* @returns {string} The new configuration ID which the config doc can be stored under.
|
||||
*/
|
||||
exports.generateConfigID = (type = "", group = "", user = "") => {
|
||||
// group += SEPARATOR
|
||||
const scope = [type, group, user].join(SEPARATOR)
|
||||
exports.generateConfigID = ({ type, group, user }) => {
|
||||
const scope = [type, group, user].filter(Boolean).join(SEPARATOR)
|
||||
|
||||
return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}${newid()}`
|
||||
return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets parameters for retrieving configurations.
|
||||
*/
|
||||
exports.getConfigParams = (type = "", group = "", otherProps = {}) => {
|
||||
exports.getConfigParams = ({ type, group, user }, otherProps = {}) => {
|
||||
const scope = [type, group, user].filter(Boolean).join(SEPARATOR)
|
||||
|
||||
return {
|
||||
...otherProps,
|
||||
startkey: `${DocumentTypes.CONFIG}${SEPARATOR}${type}${SEPARATOR}${group}`,
|
||||
endkey: `${DocumentTypes.CONFIG}${SEPARATOR}${type}${SEPARATOR}${group}${UNICODE_MAX}`,
|
||||
startkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}`,
|
||||
endkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}${UNICODE_MAX}`,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,4 @@ module.exports = {
|
|||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
COUCH_DB_URL: process.env.COUCH_DB_URL,
|
||||
SALT_ROUNDS: process.env.SALT_ROUNDS,
|
||||
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
|
||||
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
||||
GOOGLE_AUTH_CALLBACK_URL: process.env.GOOGLE_AUTH_CALLBACK_URL,
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ const {
|
|||
// Strategies
|
||||
passport.use(new LocalStrategy(local.options, local.authenticate))
|
||||
passport.use(new JwtStrategy(jwt.options, jwt.authenticate))
|
||||
// passport.use(new GoogleStrategy(google.options, google.authenticate))
|
||||
|
||||
passport.serializeUser((user, done) => done(null, user))
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
const { Cookies } = require("../constants")
|
||||
const database = require("../db")
|
||||
const { getCookie } = require("../utils")
|
||||
const { StaticDatabases } = require("../db/utils")
|
||||
|
||||
module.exports = (noAuthPatterns = []) => {
|
||||
const regex = new RegExp(noAuthPatterns.join("|"))
|
||||
|
@ -13,8 +15,11 @@ 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 = authCookie
|
||||
ctx.user = user
|
||||
}
|
||||
|
||||
return next()
|
||||
|
|
|
@ -2,17 +2,7 @@ const env = require("../../environment")
|
|||
const jwt = require("jsonwebtoken")
|
||||
const database = require("../../db")
|
||||
const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy
|
||||
const {
|
||||
StaticDatabases,
|
||||
generateUserID,
|
||||
generateGlobalUserID,
|
||||
} = require("../../db/utils")
|
||||
|
||||
exports.options = {
|
||||
clientID: env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
||||
callbackURL: env.GOOGLE_AUTH_CALLBACK_URL,
|
||||
}
|
||||
const { StaticDatabases, generateGlobalUserID } = require("../../db/utils")
|
||||
|
||||
async function authenticate(token, tokenSecret, profile, done) {
|
||||
// Check the user exists in the instance DB by email
|
||||
|
@ -58,16 +48,14 @@ async function authenticate(token, tokenSecret, profile, done) {
|
|||
|
||||
/**
|
||||
* Create an instance of the google passport strategy. This wrapper fetches the configuration
|
||||
* from couchDB rather than environment variables, and is necessary for dynamically configuring passport.
|
||||
* @returns Passport Google Strategy
|
||||
* 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() {
|
||||
exports.strategyFactory = async function(scope) {
|
||||
try {
|
||||
const db = database.getDB(StaticDatabases.GLOBAL.name)
|
||||
|
||||
const config = await db.get(
|
||||
"config_google__767bd8f363854dfa8752f593a637b3fd"
|
||||
)
|
||||
const config = await db.get(scope)
|
||||
|
||||
const { clientID, clientSecret, callbackURL } = config
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ exports.createMetadata = async function(ctx) {
|
|||
|
||||
exports.updateSelfMetadata = async function(ctx) {
|
||||
// overwrite the ID with current users
|
||||
ctx.request.body._id = ctx.user.userId
|
||||
ctx.request.body._id = ctx.user._id
|
||||
// make sure no stale rev
|
||||
delete ctx.request.body._rev
|
||||
await exports.updateMetadata(ctx)
|
||||
|
|
|
@ -31,7 +31,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.userId)
|
||||
const globalId = getGlobalIDFromUserMetadataID(ctx.user._id)
|
||||
const globalUser = await getGlobalUsers(ctx, requestAppId, globalId)
|
||||
updateCookie = true
|
||||
appId = requestAppId
|
||||
|
@ -50,7 +50,7 @@ module.exports = async (ctx, next) => {
|
|||
ctx.appId = appId
|
||||
if (roleId) {
|
||||
ctx.roleId = roleId
|
||||
const userId = ctx.user ? generateUserMetadataID(ctx.user.userId) : null
|
||||
const userId = ctx.user ? generateUserMetadataID(ctx.user._id) : null
|
||||
ctx.user = {
|
||||
...ctx.user,
|
||||
// override userID with metadata one
|
||||
|
|
|
@ -1,53 +1,27 @@
|
|||
const CouchDB = require("../../../db")
|
||||
const { StaticDatabases, DocumentTypes } = require("@budibase/auth")
|
||||
const { StaticDatabases } = require("@budibase/auth")
|
||||
const { generateConfigID, getConfigParams } = require("@budibase/auth")
|
||||
const { SEPARATOR } = require("@budibase/auth/src/db/utils")
|
||||
const { Configs } = require("../../../constants")
|
||||
|
||||
const GLOBAL_DB = StaticDatabases.GLOBAL.name
|
||||
|
||||
exports.configStatus = async function(ctx) {
|
||||
const db = new CouchDB(GLOBAL_DB)
|
||||
let configured = {}
|
||||
|
||||
// check for super admin user
|
||||
try {
|
||||
configured.user = true
|
||||
} catch (err) {
|
||||
configured.user = false
|
||||
}
|
||||
|
||||
// check for SMTP config
|
||||
try {
|
||||
const response = await db.allDocs(
|
||||
getConfigParams(`${DocumentTypes.CONFIG}${SEPARATOR}${Configs.SMTP}`)
|
||||
)
|
||||
console.log(response)
|
||||
configured.smtp = true
|
||||
} catch (err) {
|
||||
configured.smtp = false
|
||||
}
|
||||
|
||||
ctx.body = configured
|
||||
}
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const db = new CouchDB(GLOBAL_DB)
|
||||
const configDoc = ctx.request.body
|
||||
const { type, group, user } = configDoc
|
||||
|
||||
// Config does not exist yet
|
||||
if (!configDoc._id) {
|
||||
configDoc._id = generateConfigID(
|
||||
configDoc.type,
|
||||
configDoc.group,
|
||||
configDoc.user
|
||||
)
|
||||
configDoc._id = generateConfigID({
|
||||
type,
|
||||
group,
|
||||
user,
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await db.post(configDoc)
|
||||
ctx.body = {
|
||||
type: configDoc.type,
|
||||
type,
|
||||
_id: response.id,
|
||||
_rev: response.rev,
|
||||
}
|
||||
|
@ -67,18 +41,74 @@ exports.fetch = async function(ctx) {
|
|||
ctx.body = groups
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most granular config for a particular configuration type.
|
||||
* The hierarchy is type -> group -> user.
|
||||
*/
|
||||
exports.find = async function(ctx) {
|
||||
const db = new CouchDB(GLOBAL_DB)
|
||||
const response = await db.allDocs(
|
||||
getConfigParams(undefined, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
const groups = response.rows.map(row => row.doc)
|
||||
ctx.body = groups
|
||||
const userId = ctx.params.user && ctx.params.user._id
|
||||
|
||||
const { group } = ctx.query
|
||||
if (group) {
|
||||
const group = await db.get(group)
|
||||
const userInGroup = group.users.some(groupUser => groupUser === userId)
|
||||
if (!ctx.user.admin && !userInGroup) {
|
||||
ctx.throw(400, `User is not in specified group: ${group}.`)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const record = await db.get(ctx.params.id)
|
||||
ctx.body = record
|
||||
const response = await db.allDocs(
|
||||
getConfigParams(
|
||||
{
|
||||
type: ctx.params.type,
|
||||
user: userId,
|
||||
group,
|
||||
},
|
||||
{
|
||||
include_docs: true,
|
||||
}
|
||||
)
|
||||
)
|
||||
const configs = response.rows.map(row => row.doc)
|
||||
|
||||
// Find the config with the most granular scope based on context
|
||||
const scopedConfig = configs.find(config => {
|
||||
// Config is specific to a user and a group
|
||||
if (
|
||||
config._id.includes(
|
||||
generateConfigID({ type: ctx.params.type, user: userId, group })
|
||||
)
|
||||
) {
|
||||
return config
|
||||
}
|
||||
|
||||
// Config is specific to a user
|
||||
if (
|
||||
config._id.includes(
|
||||
generateConfigID({ type: ctx.params.type, user: userId })
|
||||
)
|
||||
) {
|
||||
return config
|
||||
}
|
||||
|
||||
// Config is specific to a group only
|
||||
if (
|
||||
config._id.includes(generateConfigID({ type: ctx.params.type, group }))
|
||||
) {
|
||||
return config
|
||||
}
|
||||
|
||||
// Config specific to a config type only
|
||||
return config
|
||||
})
|
||||
|
||||
if (scopedConfig) {
|
||||
ctx.body = scopedConfig
|
||||
} else {
|
||||
ctx.throw(400, "No configuration exists.")
|
||||
}
|
||||
} catch (err) {
|
||||
ctx.throw(err.status, err)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const authPkg = require("@budibase/auth")
|
||||
const { google } = require("@budibase/auth/src/middleware")
|
||||
const { Configs } = require("../../constants")
|
||||
const { clearCookie } = authPkg.utils
|
||||
const { Cookies } = authPkg
|
||||
const { passport } = authPkg.auth
|
||||
|
@ -35,8 +36,16 @@ exports.logout = async ctx => {
|
|||
ctx.body = { message: "User logged out" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial call that google authentication makes to take you to the google login screen.
|
||||
* On a successful login, you will be redirected to the googleAuth callback route.
|
||||
*/
|
||||
exports.googlePreAuth = async (ctx, next) => {
|
||||
const strategy = await google.strategyFactory()
|
||||
const strategy = await google.strategyFactory({
|
||||
type: Configs.GOOGLE,
|
||||
user: ctx.user._id,
|
||||
group: ctx.query.group,
|
||||
})
|
||||
|
||||
return passport.authenticate(strategy, {
|
||||
scope: ["profile", "email"],
|
||||
|
@ -44,7 +53,7 @@ exports.googlePreAuth = async (ctx, next) => {
|
|||
}
|
||||
|
||||
exports.googleAuth = async (ctx, next) => {
|
||||
const strategy = await google.strategyFactory()
|
||||
const strategy = await google.strategyFactory(ctx)
|
||||
|
||||
return passport.authenticate(
|
||||
strategy,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const Router = require("@koa/router")
|
||||
const controller = require("../../controllers/admin/configs")
|
||||
const joiValidator = require("../../../middleware/joi-validator")
|
||||
const { authenticated } = require("@budibase/auth")
|
||||
const Joi = require("joi")
|
||||
const { Configs } = require("../../../constants")
|
||||
|
||||
|
@ -16,9 +15,8 @@ function buildConfigSaveValidation() {
|
|||
|
||||
router
|
||||
.post("/api/admin/configs", buildConfigSaveValidation(), controller.save)
|
||||
.post("/api/admin/configs/status", controller.configStatus)
|
||||
.delete("/api/admin/configs/:id", controller.destroy)
|
||||
.get("/api/admin/configs", controller.fetch)
|
||||
.get("/api/admin/configs/:id", controller.find)
|
||||
.get("/api/admin/configs/:type", controller.find)
|
||||
|
||||
module.exports = router
|
||||
|
|
Loading…
Reference in New Issue