scoped configuration management
This commit is contained in:
parent
caa4f0dfb7
commit
9a12239e62
|
@ -98,7 +98,7 @@ exports.getTemplateParams = (ownerId, templateId, otherProps = {}) => {
|
||||||
* Generates a new configuration ID.
|
* Generates a new configuration ID.
|
||||||
* @returns {string} The new configuration ID which the config doc can be stored under.
|
* @returns {string} The new configuration ID which the config doc can be stored under.
|
||||||
*/
|
*/
|
||||||
exports.generateConfigID = ({ type, group, user }) => {
|
const generateConfigID = ({ type, group, user }) => {
|
||||||
const scope = [type, group, user].filter(Boolean).join(SEPARATOR)
|
const scope = [type, group, user].filter(Boolean).join(SEPARATOR)
|
||||||
|
|
||||||
return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}`
|
return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}`
|
||||||
|
@ -107,7 +107,7 @@ exports.generateConfigID = ({ type, group, user }) => {
|
||||||
/**
|
/**
|
||||||
* Gets parameters for retrieving configurations.
|
* Gets parameters for retrieving configurations.
|
||||||
*/
|
*/
|
||||||
exports.getConfigParams = ({ type, group, user }, otherProps = {}) => {
|
const getConfigParams = ({ type, group, user }, otherProps = {}) => {
|
||||||
const scope = [type, group, user].filter(Boolean).join(SEPARATOR)
|
const scope = [type, group, user].filter(Boolean).join(SEPARATOR)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -116,3 +116,48 @@ exports.getConfigParams = ({ type, group, user }, otherProps = {}) => {
|
||||||
endkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}${UNICODE_MAX}`,
|
endkey: `${DocumentTypes.CONFIG}${SEPARATOR}${scope}${UNICODE_MAX}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most granular configuration document from the DB based on the type, group and userID passed.
|
||||||
|
* @param {*} db - db instance to quer
|
||||||
|
* @param {Object} scopes - the type, group and userID scopes of the configuration.
|
||||||
|
* @returns The most granular configuration document based on the scope.
|
||||||
|
*/
|
||||||
|
const determineScopedConfig = async function(db, { type, user, group }) {
|
||||||
|
const response = await db.allDocs(
|
||||||
|
getConfigParams(
|
||||||
|
{ type, user, 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, user, group }))) {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config is specific to a user
|
||||||
|
if (config._id.includes(generateConfigID({ type, user }))) {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config is specific to a group only
|
||||||
|
if (config._id.includes(generateConfigID({ type, group }))) {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config specific to a config type only
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
|
||||||
|
return scopedConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.generateConfigID = generateConfigID
|
||||||
|
exports.getConfigParams = getConfigParams
|
||||||
|
exports.determineScopedConfig = determineScopedConfig
|
||||||
|
|
|
@ -22,6 +22,7 @@ const {
|
||||||
getEmailFromUserID,
|
getEmailFromUserID,
|
||||||
generateConfigID,
|
generateConfigID,
|
||||||
getConfigParams,
|
getConfigParams,
|
||||||
|
determineScopedConfig,
|
||||||
} = require("./db/utils")
|
} = require("./db/utils")
|
||||||
|
|
||||||
// Strategies
|
// Strategies
|
||||||
|
@ -71,6 +72,7 @@ module.exports = {
|
||||||
getEmailFromUserID,
|
getEmailFromUserID,
|
||||||
generateConfigID,
|
generateConfigID,
|
||||||
getConfigParams,
|
getConfigParams,
|
||||||
|
determineScopedConfig,
|
||||||
hash,
|
hash,
|
||||||
compare,
|
compare,
|
||||||
getAppId,
|
getAppId,
|
||||||
|
|
|
@ -51,12 +51,8 @@ async function authenticate(token, tokenSecret, profile, done) {
|
||||||
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
|
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
|
||||||
* @returns Dynamically configured Passport Google Strategy
|
* @returns Dynamically configured Passport Google Strategy
|
||||||
*/
|
*/
|
||||||
exports.strategyFactory = async function(scope) {
|
exports.strategyFactory = async function(config) {
|
||||||
try {
|
try {
|
||||||
const db = database.getDB(StaticDatabases.GLOBAL.name)
|
|
||||||
|
|
||||||
const config = await db.get(scope)
|
|
||||||
|
|
||||||
const { clientID, clientSecret, callbackURL } = config
|
const { clientID, clientSecret, callbackURL } = config
|
||||||
|
|
||||||
if (!clientID || !clientSecret || !callbackURL) {
|
if (!clientID || !clientSecret || !callbackURL) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
const { StaticDatabases } = require("@budibase/auth")
|
const { StaticDatabases, determineScopedConfig } = require("@budibase/auth")
|
||||||
const { generateConfigID, getConfigParams } = require("@budibase/auth")
|
const { generateConfigID, getConfigParams } = require("@budibase/auth")
|
||||||
|
|
||||||
const GLOBAL_DB = StaticDatabases.GLOBAL.name
|
const GLOBAL_DB = StaticDatabases.GLOBAL.name
|
||||||
|
@ -59,49 +59,11 @@ exports.find = async function(ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await db.allDocs(
|
// Find the config with the most granular scope based on context
|
||||||
getConfigParams(
|
const scopedConfig = await determineScopedConfig(db, {
|
||||||
{
|
|
||||||
type: ctx.params.type,
|
type: ctx.params.type,
|
||||||
user: userId,
|
user: userId,
|
||||||
group,
|
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) {
|
if (scopedConfig) {
|
||||||
|
|
|
@ -74,6 +74,6 @@ exports.find = async ctx => {
|
||||||
|
|
||||||
exports.destroy = async ctx => {
|
exports.destroy = async ctx => {
|
||||||
// TODO
|
// TODO
|
||||||
const db = new CouchDB(GLOBAL_DB)
|
// const db = new CouchDB(GLOBAL_DB)
|
||||||
ctx.body = {}
|
ctx.body = {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
const { determineScopedConfig } = require("@budibase/auth")
|
||||||
const authPkg = require("@budibase/auth")
|
const authPkg = require("@budibase/auth")
|
||||||
const { google } = require("@budibase/auth/src/middleware")
|
const { google } = require("@budibase/auth/src/middleware")
|
||||||
const { Configs } = require("../../constants")
|
const { Configs } = require("../../constants")
|
||||||
|
const CouchDB = require("../../db")
|
||||||
const { clearCookie } = authPkg.utils
|
const { clearCookie } = authPkg.utils
|
||||||
const { Cookies } = authPkg
|
const { Cookies } = authPkg
|
||||||
const { passport } = authPkg.auth
|
const { passport } = authPkg.auth
|
||||||
|
|
||||||
|
const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name
|
||||||
|
|
||||||
exports.authenticate = async (ctx, next) => {
|
exports.authenticate = async (ctx, next) => {
|
||||||
return passport.authenticate("local", async (err, user) => {
|
return passport.authenticate("local", async (err, user) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -41,11 +45,12 @@ exports.logout = async ctx => {
|
||||||
* On a successful login, you will be redirected to the googleAuth callback route.
|
* On a successful login, you will be redirected to the googleAuth callback route.
|
||||||
*/
|
*/
|
||||||
exports.googlePreAuth = async (ctx, next) => {
|
exports.googlePreAuth = async (ctx, next) => {
|
||||||
const strategy = await google.strategyFactory({
|
const db = new CouchDB(GLOBAL_DB)
|
||||||
|
const config = await determineScopedConfig(db, {
|
||||||
type: Configs.GOOGLE,
|
type: Configs.GOOGLE,
|
||||||
user: ctx.user._id,
|
|
||||||
group: ctx.query.group,
|
group: ctx.query.group,
|
||||||
})
|
})
|
||||||
|
const strategy = await google.strategyFactory(config)
|
||||||
|
|
||||||
return passport.authenticate(strategy, {
|
return passport.authenticate(strategy, {
|
||||||
scope: ["profile", "email"],
|
scope: ["profile", "email"],
|
||||||
|
@ -53,11 +58,13 @@ exports.googlePreAuth = async (ctx, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.googleAuth = async (ctx, next) => {
|
exports.googleAuth = async (ctx, next) => {
|
||||||
const strategy = await google.strategyFactory({
|
const db = new CouchDB(GLOBAL_DB)
|
||||||
|
|
||||||
|
const config = await determineScopedConfig(db, {
|
||||||
type: Configs.GOOGLE,
|
type: Configs.GOOGLE,
|
||||||
user: ctx.user._id,
|
|
||||||
group: ctx.query.group,
|
group: ctx.query.group,
|
||||||
})
|
})
|
||||||
|
const strategy = await google.strategyFactory(config)
|
||||||
|
|
||||||
return passport.authenticate(
|
return passport.authenticate(
|
||||||
strategy,
|
strategy,
|
||||||
|
|
Loading…
Reference in New Issue