access levels
This commit is contained in:
parent
e9fab13750
commit
fe79f294b4
|
@ -1,6 +1,6 @@
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
|
|
||||||
export async function createUser(user, appId, instanceId) {
|
export async function createUser(user, instanceId) {
|
||||||
const CREATE_USER_URL = `/api/${instanceId}/users`
|
const CREATE_USER_URL = `/api/${instanceId}/users`
|
||||||
const response = await api.post(CREATE_USER_URL, user)
|
const response = await api.post(CREATE_USER_URL, user)
|
||||||
return await response.json()
|
return await response.json()
|
||||||
|
@ -15,20 +15,20 @@ export async function createDatabase(appname, instanceName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteRecord(record, instanceId) {
|
export async function deleteRecord(record, instanceId) {
|
||||||
const DELETE_RECORDS_URL = `/api/${instanceId}/records/${record._id}/${record._rev}`
|
const DELETE_RECORDS_URL = `/api/${instanceId}/${record.modelId}/records/${record._id}/${record._rev}`
|
||||||
const response = await api.delete(DELETE_RECORDS_URL)
|
const response = await api.delete(DELETE_RECORDS_URL)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveRecord(record, instanceId) {
|
export async function saveRecord(record, instanceId) {
|
||||||
const SAVE_RECORDS_URL = `/api/${instanceId}/records`
|
const SAVE_RECORDS_URL = `/api/${instanceId}/${record.modelId}/records`
|
||||||
const response = await api.post(SAVE_RECORDS_URL, record)
|
const response = await api.post(SAVE_RECORDS_URL, record)
|
||||||
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchDataForView(viewName, instanceId) {
|
export async function fetchDataForView(viewName, instanceId) {
|
||||||
const FETCH_RECORDS_URL = `/api/${instanceId}/${viewName}/records`
|
const FETCH_RECORDS_URL = `/api/${instanceId}/views/${viewName}`
|
||||||
|
|
||||||
const response = await api.get(FETCH_RECORDS_URL)
|
const response = await api.get(FETCH_RECORDS_URL)
|
||||||
return await response.json()
|
return await response.json()
|
||||||
|
|
|
@ -7,14 +7,15 @@
|
||||||
|
|
||||||
let username
|
let username
|
||||||
let password
|
let password
|
||||||
|
let accessLevelId
|
||||||
|
|
||||||
$: valid = username && password
|
$: valid = username && password && accessLevelId
|
||||||
$: instanceId = $backendUiStore.selectedDatabase._id
|
$: instanceId = $backendUiStore.selectedDatabase._id
|
||||||
$: appId = $store.appId
|
$: appId = $store.appId
|
||||||
|
|
||||||
async function createUser() {
|
async function createUser() {
|
||||||
const user = { name: username, username, password }
|
const user = { name: username, username, password, accessLevelId }
|
||||||
const response = await api.createUser(user, appId, instanceId)
|
const response = await api.createUser(user, instanceId)
|
||||||
backendUiStore.actions.users.create(response)
|
backendUiStore.actions.users.create(response)
|
||||||
onClosed()
|
onClosed()
|
||||||
}
|
}
|
||||||
|
@ -30,6 +31,14 @@
|
||||||
<label class="uk-form-label" for="form-stacked-text">Password</label>
|
<label class="uk-form-label" for="form-stacked-text">Password</label>
|
||||||
<input class="uk-input" type="password" bind:value={password} />
|
<input class="uk-input" type="password" bind:value={password} />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label" for="form-stacked-text">Access Level</label>
|
||||||
|
<select class="uk-select" bind:value={accessLevelId}>
|
||||||
|
<option value=""></option>
|
||||||
|
<option value="POWER_USER">Power User</option>
|
||||||
|
<option value="ADMIN">Admin</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||||
|
|
|
@ -11,13 +11,6 @@ module.exports = {
|
||||||
default: "~/.budibase",
|
default: "~/.budibase",
|
||||||
alias: "d",
|
alias: "d",
|
||||||
})
|
})
|
||||||
yargs.positional("database", {
|
|
||||||
type: "string",
|
|
||||||
describe: "use a local (PouchDB) or remote (CouchDB) database",
|
|
||||||
alias: "b",
|
|
||||||
default: "local",
|
|
||||||
choices: ["local", "remote"],
|
|
||||||
})
|
|
||||||
yargs.positional("clientId", {
|
yargs.positional("clientId", {
|
||||||
type: "string",
|
type: "string",
|
||||||
describe: "used to determine the name of the global databse",
|
describe: "used to determine the name of the global databse",
|
||||||
|
@ -28,7 +21,7 @@ module.exports = {
|
||||||
type: "string",
|
type: "string",
|
||||||
describe:
|
describe:
|
||||||
"connection string for couch db, format: https://username:password@localhost:5984",
|
"connection string for couch db, format: https://username:password@localhost:5984",
|
||||||
alias: "x",
|
alias: "u",
|
||||||
default: "",
|
default: "",
|
||||||
})
|
})
|
||||||
yargs.positional("quiet", {
|
yargs.positional("quiet", {
|
||||||
|
|
|
@ -14,7 +14,6 @@ const run = async opts => {
|
||||||
try {
|
try {
|
||||||
await ensureAppDir(opts)
|
await ensureAppDir(opts)
|
||||||
await setEnvironmentVariables(opts)
|
await setEnvironmentVariables(opts)
|
||||||
await prompts(opts)
|
|
||||||
await createClientDatabase(opts)
|
await createClientDatabase(opts)
|
||||||
await createDevEnvFile(opts)
|
await createDevEnvFile(opts)
|
||||||
console.log(chalk.green("Budibase successfully initialised."))
|
console.log(chalk.green("Budibase successfully initialised."))
|
||||||
|
@ -24,13 +23,13 @@ const run = async opts => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const setEnvironmentVariables = async opts => {
|
const setEnvironmentVariables = async opts => {
|
||||||
if (opts.database === "local") {
|
if (opts.couchDbUrl) {
|
||||||
|
process.env.COUCH_DB_URL = opts.couchDbUrl
|
||||||
|
} else {
|
||||||
const dataDir = join(opts.dir, ".data")
|
const dataDir = join(opts.dir, ".data")
|
||||||
await ensureDir(dataDir)
|
await ensureDir(dataDir)
|
||||||
process.env.COUCH_DB_URL =
|
process.env.COUCH_DB_URL =
|
||||||
dataDir + (dataDir.endsWith("/") || dataDir.endsWith("\\") ? "" : "/")
|
dataDir + (dataDir.endsWith("/") || dataDir.endsWith("\\") ? "" : "/")
|
||||||
} else {
|
|
||||||
process.env.COUCH_DB_URL = opts.couchDbUrl
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,25 +38,6 @@ const ensureAppDir = async opts => {
|
||||||
await ensureDir(opts.dir)
|
await ensureDir(opts.dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
const prompts = async opts => {
|
|
||||||
const questions = [
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
name: "couchDbUrl",
|
|
||||||
message:
|
|
||||||
"CouchDB Connection String (e.g. https://user:password@localhost:5984): ",
|
|
||||||
validate: function(value) {
|
|
||||||
return !!value || "Please enter connection string"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
if (opts.database === "remote" && !opts.couchDbUrl) {
|
|
||||||
const answers = await inquirer.prompt(questions)
|
|
||||||
opts.couchDbUrl = answers.couchDbUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createClientDatabase = async opts => {
|
const createClientDatabase = async opts => {
|
||||||
// cannot be a top level require as it
|
// cannot be a top level require as it
|
||||||
// will cause environment module to be loaded prematurely
|
// will cause environment module to be loaded prematurely
|
||||||
|
|
|
@ -49,6 +49,19 @@
|
||||||
"program": "${workspaceFolder}/node_modules/jest-cli/bin/jest",
|
"program": "${workspaceFolder}/node_modules/jest-cli/bin/jest",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Jest - Access Levels",
|
||||||
|
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||||
|
"args": ["accesslevel.spec", "--runInBand"],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen",
|
||||||
|
"disableOptimisticBPs": true,
|
||||||
|
"windows": {
|
||||||
|
"program": "${workspaceFolder}/node_modules/jest-cli/bin/jest",
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
const CouchDB = require("../../db")
|
||||||
|
const newid = require("../../db/newid")
|
||||||
|
const {
|
||||||
|
generateAdminPermissions,
|
||||||
|
generatePowerUserPermissions,
|
||||||
|
POWERUSER_LEVEL_ID,
|
||||||
|
ADMIN_LEVEL_ID,
|
||||||
|
} = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
|
exports.fetch = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
|
const body = await db.query("database/by_type", {
|
||||||
|
include_docs: true,
|
||||||
|
key: ["accesslevel"],
|
||||||
|
})
|
||||||
|
const customAccessLevels = body.rows.map(row => row.doc)
|
||||||
|
|
||||||
|
const staticAccessLevels = [
|
||||||
|
{
|
||||||
|
_id: ADMIN_LEVEL_ID,
|
||||||
|
name: "Admin",
|
||||||
|
permissions: await generateAdminPermissions(ctx.params.instanceId),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: POWERUSER_LEVEL_ID,
|
||||||
|
name: "Power User",
|
||||||
|
permissions: await generatePowerUserPermissions(ctx.params.instanceId),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
ctx.body = [...staticAccessLevels, ...customAccessLevels]
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.find = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
|
ctx.body = await db.get(ctx.params.levelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.update = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
|
const level = await db.get(ctx.params.levelId)
|
||||||
|
level.name = ctx.body.name
|
||||||
|
level.permissions = ctx.request.body.permissions
|
||||||
|
const result = await db.put(level)
|
||||||
|
level._rev = result.rev
|
||||||
|
ctx.body = level
|
||||||
|
ctx.message = `Level ${level.name} updated successfully.`
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.patch = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
|
const level = await db.get(ctx.params.levelId)
|
||||||
|
const { removedPermissions, addedPermissions, _rev } = ctx.request.body
|
||||||
|
|
||||||
|
if (!_rev) throw new Error("Must supply a _rev to update an access level")
|
||||||
|
|
||||||
|
level._rev = _rev
|
||||||
|
|
||||||
|
if (removedPermissions) {
|
||||||
|
level.permissions = level.permissions.filter(
|
||||||
|
p =>
|
||||||
|
!removedPermissions.some(
|
||||||
|
rem => rem.name === p.name && rem.itemId === p.itemId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addedPermissions) {
|
||||||
|
level.permissions = [
|
||||||
|
...level.permissions.filter(
|
||||||
|
p =>
|
||||||
|
!addedPermissions.some(
|
||||||
|
add => add.name === p.name && add.itemId === p.itemId
|
||||||
|
)
|
||||||
|
),
|
||||||
|
...addedPermissions,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await db.put(level)
|
||||||
|
level._rev = result.rev
|
||||||
|
ctx.body = level
|
||||||
|
ctx.message = `Access Level ${level.name} updated successfully.`
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.create = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
|
|
||||||
|
const level = {
|
||||||
|
name: ctx.request.body.name,
|
||||||
|
_rev: ctx.request.body._rev,
|
||||||
|
permissions: ctx.request.body.permissions || [],
|
||||||
|
_id: newid(),
|
||||||
|
type: "accesslevel",
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await db.put(level)
|
||||||
|
level._rev = result.rev
|
||||||
|
ctx.body = level
|
||||||
|
ctx.message = `Access Level '${level.name}' created successfully.`
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.destroy = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
|
await db.remove(ctx.params.levelId, ctx.params.rev)
|
||||||
|
ctx.message = `Access Level ${ctx.params.id} deleted successfully`
|
||||||
|
ctx.status = 200
|
||||||
|
}
|
|
@ -24,20 +24,21 @@ exports.authenticate = async ctx => {
|
||||||
|
|
||||||
// Check the user exists in the instance DB by username
|
// Check the user exists in the instance DB by username
|
||||||
const instanceDb = new CouchDB(instanceId)
|
const instanceDb = new CouchDB(instanceId)
|
||||||
const { rows } = await instanceDb.query("database/by_username", {
|
|
||||||
include_docs: true,
|
|
||||||
username,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (rows.length === 0) ctx.throw(500, `User does not exist.`)
|
let dbUser
|
||||||
|
try {
|
||||||
const dbUser = rows[0].doc
|
dbUser = await instanceDb.get(`user_${username}`)
|
||||||
|
} catch (_) {
|
||||||
|
// do not want to throw a 404 - as this could be
|
||||||
|
// used to dtermine valid usernames
|
||||||
|
ctx.throw(401, "Invalid Credentials")
|
||||||
|
}
|
||||||
|
|
||||||
// authenticate
|
// authenticate
|
||||||
if (await bcrypt.compare(password, dbUser.password)) {
|
if (await bcrypt.compare(password, dbUser.password)) {
|
||||||
const payload = {
|
const payload = {
|
||||||
userId: dbUser._id,
|
userId: dbUser._id,
|
||||||
accessLevel: "",
|
accessLevelId: dbUser.accessLevelId,
|
||||||
instanceId: instanceId,
|
instanceId: instanceId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ exports.save = async function(ctx) {
|
||||||
console.log("THIS INSTANCE", ctx.params.instanceId);
|
console.log("THIS INSTANCE", ctx.params.instanceId);
|
||||||
const db = new CouchDB(ctx.params.instanceId)
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
const record = ctx.request.body
|
const record = ctx.request.body
|
||||||
|
record.modelId = ctx.params.modelId
|
||||||
|
|
||||||
if (!record._rev && !record._id) {
|
if (!record._rev && !record._id) {
|
||||||
record._id = newid()
|
record._id = newid()
|
||||||
|
@ -45,13 +46,12 @@ exports.save = async function(ctx) {
|
||||||
const response = await db.post(record)
|
const response = await db.post(record)
|
||||||
record._rev = response.rev
|
record._rev = response.rev
|
||||||
// ctx.eventPublisher.emit("RECORD_CREATED", record)
|
// ctx.eventPublisher.emit("RECORD_CREATED", record)
|
||||||
|
|
||||||
ctx.body = record
|
ctx.body = record
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.message = `${model.name} created successfully`
|
ctx.message = `${model.name} created successfully`
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async function(ctx) {
|
exports.fetchView = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.params.instanceId)
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
const response = await db.query(`database/${ctx.params.viewName}`, {
|
const response = await db.query(`database/${ctx.params.viewName}`, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -59,13 +59,30 @@ exports.fetch = async function(ctx) {
|
||||||
ctx.body = response.rows.map(row => row.doc)
|
ctx.body = response.rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.fetchModel = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
|
const response = await db.query(`database/all_${ctx.params.modelId}`, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
ctx.body = response.rows.map(row => row.doc)
|
||||||
|
}
|
||||||
|
|
||||||
exports.find = async function(ctx) {
|
exports.find = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.params.instanceId)
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
ctx.body = await db.get(ctx.params.recordId)
|
const record = await db.get(ctx.params.recordId)
|
||||||
|
if (record.modelId !== ctx.params.modelId) {
|
||||||
|
ctx.throw(400, "Supplied modelId doe not match the record's modelId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.body = record
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function(ctx) {
|
exports.destroy = async function(ctx) {
|
||||||
const databaseId = ctx.params.instanceId
|
const db = new CouchDB(ctx.params.instanceId)
|
||||||
const db = new CouchDB(databaseId)
|
const record = await db.get(ctx.params.recordId)
|
||||||
|
if (record.modelId !== ctx.params.modelId) {
|
||||||
|
ctx.throw(400, "Supplied modelId doe not match the record's modelId")
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId)
|
ctx.body = await db.remove(ctx.params.recordId, ctx.params.revId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,11 @@ const CouchDB = require("../../db")
|
||||||
const clientDb = require("../../db/clientDb")
|
const clientDb = require("../../db/clientDb")
|
||||||
const bcrypt = require("../../utilities/bcrypt")
|
const bcrypt = require("../../utilities/bcrypt")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
|
|
||||||
const getUserId = userName => `user_${userName}`
|
const getUserId = userName => `user_${userName}`
|
||||||
|
const {
|
||||||
|
POWERUSER_LEVEL_ID,
|
||||||
|
ADMIN_LEVEL_ID,
|
||||||
|
} = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
exports.fetch = async function(ctx) {
|
exports.fetch = async function(ctx) {
|
||||||
const database = new CouchDB(ctx.params.instanceId)
|
const database = new CouchDB(ctx.params.instanceId)
|
||||||
|
@ -18,17 +21,26 @@ exports.fetch = async function(ctx) {
|
||||||
exports.create = async function(ctx) {
|
exports.create = async function(ctx) {
|
||||||
const database = new CouchDB(ctx.params.instanceId)
|
const database = new CouchDB(ctx.params.instanceId)
|
||||||
const appId = (await database.get("_design/database")).metadata.applicationId
|
const appId = (await database.get("_design/database")).metadata.applicationId
|
||||||
const { username, password, name } = ctx.request.body
|
const { username, password, name, accessLevelId } = ctx.request.body
|
||||||
|
|
||||||
if (!username || !password) ctx.throw(400, "Username and Password Required.")
|
if (!username || !password) {
|
||||||
|
ctx.throw(400, "Username and Password Required.")
|
||||||
|
}
|
||||||
|
|
||||||
const response = await database.post({
|
const accessLevel = await checkAccessLevel(database, accessLevelId)
|
||||||
|
|
||||||
|
if (!accessLevel) ctx.throw(400, "Invalid Access Level")
|
||||||
|
|
||||||
|
const user = {
|
||||||
_id: getUserId(username),
|
_id: getUserId(username),
|
||||||
username,
|
username,
|
||||||
password: await bcrypt.hash(password),
|
password: await bcrypt.hash(password),
|
||||||
name: name || username,
|
name: name || username,
|
||||||
type: "user",
|
type: "user",
|
||||||
})
|
accessLevelId,
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await database.post(user)
|
||||||
|
|
||||||
// the clientDB needs to store a map of users against the app
|
// the clientDB needs to store a map of users against the app
|
||||||
const db = new CouchDB(clientDb.name(env.CLIENT_ID))
|
const db = new CouchDB(clientDb.name(env.CLIENT_ID))
|
||||||
|
@ -49,6 +61,8 @@ exports.create = async function(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.update = async function(ctx) {}
|
||||||
|
|
||||||
exports.destroy = async function(ctx) {
|
exports.destroy = async function(ctx) {
|
||||||
const database = new CouchDB(ctx.params.instanceId)
|
const database = new CouchDB(ctx.params.instanceId)
|
||||||
await database.destroy(getUserId(ctx.params.username))
|
await database.destroy(getUserId(ctx.params.username))
|
||||||
|
@ -65,3 +79,18 @@ exports.find = async function(ctx) {
|
||||||
_rev: user._rev,
|
_rev: user._rev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkAccessLevel = async (db, accessLevelId) => {
|
||||||
|
if (!accessLevelId) return
|
||||||
|
if (
|
||||||
|
accessLevelId === POWERUSER_LEVEL_ID ||
|
||||||
|
accessLevelId === ADMIN_LEVEL_ID
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
_id: accessLevelId,
|
||||||
|
name: accessLevelId,
|
||||||
|
permissions: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await db.get(accessLevelId)
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ const {
|
||||||
authRoutes,
|
authRoutes,
|
||||||
pageRoutes,
|
pageRoutes,
|
||||||
userRoutes,
|
userRoutes,
|
||||||
recordRoutes,
|
|
||||||
instanceRoutes,
|
instanceRoutes,
|
||||||
clientRoutes,
|
clientRoutes,
|
||||||
applicationRoutes,
|
applicationRoutes,
|
||||||
|
@ -15,7 +14,8 @@ const {
|
||||||
viewRoutes,
|
viewRoutes,
|
||||||
staticRoutes,
|
staticRoutes,
|
||||||
componentRoutes,
|
componentRoutes,
|
||||||
workflowRoutes
|
workflowRoutes,
|
||||||
|
accesslevelRoutes,
|
||||||
} = require("./routes")
|
} = require("./routes")
|
||||||
|
|
||||||
const router = new Router()
|
const router = new Router()
|
||||||
|
@ -71,9 +71,6 @@ router.use(modelRoutes.allowedMethods())
|
||||||
router.use(userRoutes.routes())
|
router.use(userRoutes.routes())
|
||||||
router.use(userRoutes.allowedMethods())
|
router.use(userRoutes.allowedMethods())
|
||||||
|
|
||||||
router.use(recordRoutes.routes())
|
|
||||||
router.use(recordRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(instanceRoutes.routes())
|
router.use(instanceRoutes.routes())
|
||||||
router.use(instanceRoutes.allowedMethods())
|
router.use(instanceRoutes.allowedMethods())
|
||||||
|
|
||||||
|
@ -93,6 +90,9 @@ router.use(componentRoutes.allowedMethods())
|
||||||
router.use(clientRoutes.routes())
|
router.use(clientRoutes.routes())
|
||||||
router.use(clientRoutes.allowedMethods())
|
router.use(clientRoutes.allowedMethods())
|
||||||
|
|
||||||
|
router.use(accesslevelRoutes.routes())
|
||||||
|
router.use(accesslevelRoutes.allowedMethods())
|
||||||
|
|
||||||
router.use(staticRoutes.routes())
|
router.use(staticRoutes.routes())
|
||||||
router.use(staticRoutes.allowedMethods())
|
router.use(staticRoutes.allowedMethods())
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const Router = require("@koa/router")
|
||||||
|
const controller = require("../controllers/accesslevel")
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.post("/api/:instanceId/accesslevels", controller.create)
|
||||||
|
.put("/api/:instanceId/accesslevels", controller.update)
|
||||||
|
.get("/api/:instanceId/accesslevels", controller.fetch)
|
||||||
|
.get("/api/:instanceId/accesslevels/:levelId", controller.find)
|
||||||
|
.delete("/api/:instanceId/accesslevels/:levelId/:rev", controller.destroy)
|
||||||
|
.patch("/api/:instanceId/accesslevels/:levelId", controller.patch)
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -1,11 +1,17 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/application")
|
const controller = require("../controllers/application")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/applications", controller.fetch)
|
.get("/api/applications", authorized(BUILDER), controller.fetch)
|
||||||
.get("/api/:applicationId/appPackage", controller.fetchAppPackage)
|
.get(
|
||||||
.post("/api/applications", controller.create)
|
"/api/:applicationId/appPackage",
|
||||||
|
authorized(BUILDER),
|
||||||
|
controller.fetchAppPackage
|
||||||
|
)
|
||||||
|
.post("/api/applications", authorized(BUILDER), controller.create)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/client")
|
const controller = require("../controllers/client")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router.get("/api/client/id", controller.getClientId)
|
router.get("/api/client/id", authorized(BUILDER), controller.getClientId)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/component")
|
const controller = require("../controllers/component")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:appId/components/definitions",
|
"/:appId/components/definitions",
|
||||||
|
authorized(BUILDER),
|
||||||
controller.fetchAppComponentDefinitions
|
controller.fetchAppComponentDefinitions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const authRoutes = require("./auth")
|
const authRoutes = require("./auth")
|
||||||
const pageRoutes = require("./pages")
|
const pageRoutes = require("./pages")
|
||||||
const userRoutes = require("./user")
|
const userRoutes = require("./user")
|
||||||
const recordRoutes = require("./record")
|
|
||||||
const instanceRoutes = require("./instance")
|
const instanceRoutes = require("./instance")
|
||||||
const clientRoutes = require("./client")
|
const clientRoutes = require("./client")
|
||||||
const applicationRoutes = require("./application")
|
const applicationRoutes = require("./application")
|
||||||
|
@ -9,13 +8,13 @@ const modelRoutes = require("./model")
|
||||||
const viewRoutes = require("./view")
|
const viewRoutes = require("./view")
|
||||||
const staticRoutes = require("./static")
|
const staticRoutes = require("./static")
|
||||||
const componentRoutes = require("./component")
|
const componentRoutes = require("./component")
|
||||||
const workflowRoutes = require("./workflow");
|
const workflowRoutes = require("./workflow")
|
||||||
|
const accesslevelRoutes = require("./accesslevel")
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
authRoutes,
|
authRoutes,
|
||||||
pageRoutes,
|
pageRoutes,
|
||||||
userRoutes,
|
userRoutes,
|
||||||
recordRoutes,
|
|
||||||
instanceRoutes,
|
instanceRoutes,
|
||||||
clientRoutes,
|
clientRoutes,
|
||||||
applicationRoutes,
|
applicationRoutes,
|
||||||
|
@ -23,5 +22,6 @@ module.exports = {
|
||||||
viewRoutes,
|
viewRoutes,
|
||||||
staticRoutes,
|
staticRoutes,
|
||||||
componentRoutes,
|
componentRoutes,
|
||||||
workflowRoutes
|
workflowRoutes,
|
||||||
|
accesslevelRoutes,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/instance")
|
const controller = require("../controllers/instance")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/:applicationId/instances", controller.create)
|
.post("/api/:applicationId/instances", authorized(BUILDER), controller.create)
|
||||||
.delete("/api/instances/:instanceId", controller.destroy)
|
.delete("/api/instances/:instanceId", authorized(BUILDER), controller.destroy)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,12 +1,49 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/model")
|
const modelController = require("../controllers/model")
|
||||||
|
const recordController = require("../controllers/record")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const {
|
||||||
|
READ_MODEL,
|
||||||
|
WRITE_MODEL,
|
||||||
|
BUILDER,
|
||||||
|
} = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
// records
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/:instanceId/models", controller.fetch)
|
.get(
|
||||||
.post("/api/:instanceId/models", controller.create)
|
"/api/:instanceId/:modelId/records",
|
||||||
|
authorized(READ_MODEL, ctx => ctx.params.modelId),
|
||||||
|
recordController.fetchModel
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/:instanceId/:modelId/records/:recordId",
|
||||||
|
authorized(READ_MODEL, ctx => ctx.params.modelId),
|
||||||
|
recordController.find
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/:instanceId/:modelId/records",
|
||||||
|
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
|
||||||
|
recordController.save
|
||||||
|
)
|
||||||
|
.delete(
|
||||||
|
"/api/:instanceId/:modelId/records/:recordId/:revId",
|
||||||
|
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
|
||||||
|
recordController.destroy
|
||||||
|
)
|
||||||
|
|
||||||
|
// models
|
||||||
|
|
||||||
|
router
|
||||||
|
.get("/api/:instanceId/models", authorized(BUILDER), modelController.fetch)
|
||||||
|
.post("/api/:instanceId/models", authorized(BUILDER), modelController.create)
|
||||||
// .patch("/api/:instanceId/models", controller.update)
|
// .patch("/api/:instanceId/models", controller.update)
|
||||||
.delete("/api/:instanceId/models/:modelId/:revId", controller.destroy)
|
.delete(
|
||||||
|
"/api/:instanceId/models/:modelId/:revId",
|
||||||
|
authorized(BUILDER),
|
||||||
|
modelController.destroy
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -7,10 +7,15 @@ const {
|
||||||
renameScreen,
|
renameScreen,
|
||||||
deleteScreen,
|
deleteScreen,
|
||||||
} = require("../../utilities/builder")
|
} = require("../../utilities/builder")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router.post("/_builder/api/:appId/pages/:pageName", async ctx => {
|
router.post(
|
||||||
|
"/_builder/api/:appId/pages/:pageName",
|
||||||
|
authorized(BUILDER),
|
||||||
|
async ctx => {
|
||||||
await buildPage(
|
await buildPage(
|
||||||
ctx.config,
|
ctx.config,
|
||||||
ctx.params.appId,
|
ctx.params.appId,
|
||||||
|
@ -18,18 +23,26 @@ router.post("/_builder/api/:appId/pages/:pageName", async ctx => {
|
||||||
ctx.request.body
|
ctx.request.body
|
||||||
)
|
)
|
||||||
ctx.response.status = StatusCodes.OK
|
ctx.response.status = StatusCodes.OK
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
router.get("/_builder/api/:appId/pages/:pagename/screens", async ctx => {
|
router.get(
|
||||||
|
"/_builder/api/:appId/pages/:pagename/screens",
|
||||||
|
authorized(BUILDER),
|
||||||
|
async ctx => {
|
||||||
ctx.body = await listScreens(
|
ctx.body = await listScreens(
|
||||||
ctx.config,
|
ctx.config,
|
||||||
ctx.params.appId,
|
ctx.params.appId,
|
||||||
ctx.params.pagename
|
ctx.params.pagename
|
||||||
)
|
)
|
||||||
ctx.response.status = StatusCodes.OK
|
ctx.response.status = StatusCodes.OK
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
router.post("/_builder/api/:appId/pages/:pagename/screen", async ctx => {
|
router.post(
|
||||||
|
"/_builder/api/:appId/pages/:pagename/screen",
|
||||||
|
authorized(BUILDER),
|
||||||
|
async ctx => {
|
||||||
ctx.body = await saveScreen(
|
ctx.body = await saveScreen(
|
||||||
ctx.config,
|
ctx.config,
|
||||||
ctx.params.appId,
|
ctx.params.appId,
|
||||||
|
@ -37,9 +50,13 @@ router.post("/_builder/api/:appId/pages/:pagename/screen", async ctx => {
|
||||||
ctx.request.body
|
ctx.request.body
|
||||||
)
|
)
|
||||||
ctx.response.status = StatusCodes.OK
|
ctx.response.status = StatusCodes.OK
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
router.patch("/_builder/api/:appname/pages/:pagename/screen", async ctx => {
|
router.patch(
|
||||||
|
"/_builder/api/:appname/pages/:pagename/screen",
|
||||||
|
authorized(BUILDER),
|
||||||
|
async ctx => {
|
||||||
await renameScreen(
|
await renameScreen(
|
||||||
ctx.config,
|
ctx.config,
|
||||||
ctx.params.appname,
|
ctx.params.appname,
|
||||||
|
@ -48,9 +65,13 @@ router.patch("/_builder/api/:appname/pages/:pagename/screen", async ctx => {
|
||||||
ctx.request.body.newname
|
ctx.request.body.newname
|
||||||
)
|
)
|
||||||
ctx.response.status = StatusCodes.OK
|
ctx.response.status = StatusCodes.OK
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
router.delete("/_builder/api/:appname/pages/:pagename/screen/*", async ctx => {
|
router.delete(
|
||||||
|
"/_builder/api/:appname/pages/:pagename/screen/*",
|
||||||
|
authorized(BUILDER),
|
||||||
|
async ctx => {
|
||||||
const name = ctx.request.path.replace(
|
const name = ctx.request.path.replace(
|
||||||
`/_builder/api/${ctx.params.appname}/pages/${ctx.params.pagename}/screen/`,
|
`/_builder/api/${ctx.params.appname}/pages/${ctx.params.pagename}/screen/`,
|
||||||
""
|
""
|
||||||
|
@ -64,6 +85,7 @@ router.delete("/_builder/api/:appname/pages/:pagename/screen/*", async ctx => {
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx.response.status = StatusCodes.OK
|
ctx.response.status = StatusCodes.OK
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/record")
|
|
||||||
|
|
||||||
const router = Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/:instanceId/:viewName/records", controller.fetch)
|
|
||||||
.get("/api/:instanceId/records/:recordId", controller.find)
|
|
||||||
.post("/api/:instanceId/records", controller.save)
|
|
||||||
.delete("/api/:instanceId/records/:recordId/:revId", controller.destroy)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -1,11 +1,17 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/screen")
|
const controller = require("../controllers/screen")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/:instanceId/screens", controller.fetch)
|
.get("/api/:instanceId/screens", authorized(BUILDER), controller.fetch)
|
||||||
.post("/api/:instanceId/screens", controller.save)
|
.post("/api/:instanceId/screens", authorized(BUILDER), controller.save)
|
||||||
.delete("/api/:instanceId/:screenId/:revId", controller.destroy)
|
.delete(
|
||||||
|
"/api/:instanceId/:screenId/:revId",
|
||||||
|
authorized(BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
const {
|
||||||
|
createInstance,
|
||||||
|
createClientDatabase,
|
||||||
|
createApplication,
|
||||||
|
createModel,
|
||||||
|
createView,
|
||||||
|
supertest,
|
||||||
|
defaultHeaders
|
||||||
|
} = require("./couchTestUtils")
|
||||||
|
const {
|
||||||
|
generateAdminPermissions,
|
||||||
|
generatePowerUserPermissions,
|
||||||
|
POWERUSER_LEVEL_ID,
|
||||||
|
ADMIN_LEVEL_ID,
|
||||||
|
READ_MODEL,
|
||||||
|
WRITE_MODEL,
|
||||||
|
} = require("../../../utilities/accessLevels")
|
||||||
|
|
||||||
|
describe("/accesslevels", () => {
|
||||||
|
let appId
|
||||||
|
let server
|
||||||
|
let request
|
||||||
|
let instanceId
|
||||||
|
let model
|
||||||
|
let view
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
({ request, server } = await supertest())
|
||||||
|
await createClientDatabase(request);
|
||||||
|
appId = (await createApplication(request))._id
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
server.close();
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
instanceId = (await createInstance(request, appId))._id
|
||||||
|
model = await createModel(request, instanceId)
|
||||||
|
view = await createView(request, instanceId)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("create", () => {
|
||||||
|
|
||||||
|
it("returns a success message when level is successfully created", async () => {
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/${instanceId}/accesslevels`)
|
||||||
|
.send({ name: "user" })
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
expect(res.res.statusMessage).toEqual("Access Level 'user' created successfully.")
|
||||||
|
expect(res.body._id).toBeDefined()
|
||||||
|
expect(res.body._rev).toBeDefined()
|
||||||
|
expect(res.body.permissions).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fetch", () => {
|
||||||
|
|
||||||
|
it("should list custom levels, plus 2 default levels", async () => {
|
||||||
|
const createRes = await request
|
||||||
|
.post(`/api/${instanceId}/accesslevels`)
|
||||||
|
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const customLevel = createRes.body
|
||||||
|
|
||||||
|
const res = await request
|
||||||
|
.get(`/api/${instanceId}/accesslevels`)
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
expect(res.body.length).toBe(3)
|
||||||
|
|
||||||
|
const adminLevel = res.body.find(r => r._id === ADMIN_LEVEL_ID)
|
||||||
|
expect(adminLevel).toBeDefined()
|
||||||
|
expect(adminLevel.permissions).toEqual(await generateAdminPermissions(instanceId))
|
||||||
|
|
||||||
|
const powerUserLevel = res.body.find(r => r._id === POWERUSER_LEVEL_ID)
|
||||||
|
expect(powerUserLevel).toBeDefined()
|
||||||
|
expect(powerUserLevel.permissions).toEqual(await generatePowerUserPermissions(instanceId))
|
||||||
|
|
||||||
|
const customLevelFetched = res.body.find(r => r._id === customLevel._id)
|
||||||
|
expect(customLevelFetched.permissions).toEqual(customLevel.permissions)
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("destroy", () => {
|
||||||
|
it("should delete custom access level", async () => {
|
||||||
|
const createRes = await request
|
||||||
|
.post(`/api/${instanceId}/accesslevels`)
|
||||||
|
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL } ] })
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const customLevel = createRes.body
|
||||||
|
|
||||||
|
await request
|
||||||
|
.delete(`/api/${instanceId}/accesslevels/${customLevel._id}/${customLevel._rev}`)
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
await request
|
||||||
|
.get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect(404)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("patch", () => {
|
||||||
|
it("should add given permissions", async () => {
|
||||||
|
const createRes = await request
|
||||||
|
.post(`/api/${instanceId}/accesslevels`)
|
||||||
|
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const customLevel = createRes.body
|
||||||
|
|
||||||
|
await request
|
||||||
|
.patch(`/api/${instanceId}/accesslevels/${customLevel._id}`)
|
||||||
|
.send({
|
||||||
|
_rev: customLevel._rev,
|
||||||
|
addedPermissions: [ { itemId: model._id, name: WRITE_MODEL } ]
|
||||||
|
})
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const finalRes = await request
|
||||||
|
.get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
expect(finalRes.body.permissions.length).toBe(2)
|
||||||
|
expect(finalRes.body.permissions.some(p => p.name === WRITE_MODEL)).toBe(true)
|
||||||
|
expect(finalRes.body.permissions.some(p => p.name === READ_MODEL)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should remove given permissions", async () => {
|
||||||
|
const createRes = await request
|
||||||
|
.post(`/api/${instanceId}/accesslevels`)
|
||||||
|
.send({
|
||||||
|
name: "user",
|
||||||
|
permissions: [
|
||||||
|
{ itemId: model._id, name: READ_MODEL },
|
||||||
|
{ itemId: model._id, name: WRITE_MODEL },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const customLevel = createRes.body
|
||||||
|
|
||||||
|
await request
|
||||||
|
.patch(`/api/${instanceId}/accesslevels/${customLevel._id}`)
|
||||||
|
.send({
|
||||||
|
_rev: customLevel._rev,
|
||||||
|
removedPermissions: [ { itemId: model._id, name: WRITE_MODEL }]
|
||||||
|
})
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const finalRes = await request
|
||||||
|
.get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
expect(finalRes.body.permissions.length).toBe(1)
|
||||||
|
expect(finalRes.body.permissions.some(p => p.name === READ_MODEL)).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
|
@ -1,9 +1,11 @@
|
||||||
const {
|
const {
|
||||||
createClientDatabase,
|
createClientDatabase,
|
||||||
createApplication,
|
createApplication,
|
||||||
|
createInstance,
|
||||||
destroyClientDatabase,
|
destroyClientDatabase,
|
||||||
|
builderEndpointShouldBlockNormalUsers,
|
||||||
supertest,
|
supertest,
|
||||||
defaultHeaders
|
defaultHeaders,
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
|
|
||||||
describe("/applications", () => {
|
describe("/applications", () => {
|
||||||
|
@ -37,6 +39,18 @@ describe("/applications", () => {
|
||||||
expect(res.res.statusMessage).toEqual("Application My App created successfully")
|
expect(res.res.statusMessage).toEqual("Application My App created successfully")
|
||||||
expect(res.body._id).toBeDefined()
|
expect(res.body._id).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
const otherApplication = await createApplication(request)
|
||||||
|
const instance = await createInstance(request, otherApplication._id)
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/applications`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
body: { name: "My App" }
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
|
@ -53,6 +67,17 @@ describe("/applications", () => {
|
||||||
|
|
||||||
expect(res.body.length).toBe(2)
|
expect(res.body.length).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
const otherApplication = await createApplication(request)
|
||||||
|
const instance = await createInstance(request, otherApplication._id)
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/applications`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,10 @@ const CouchDB = require("../../../db")
|
||||||
const { create, destroy } = require("../../../db/clientDb")
|
const { create, destroy } = require("../../../db/clientDb")
|
||||||
const supertest = require("supertest")
|
const supertest = require("supertest")
|
||||||
const app = require("../../../app")
|
const app = require("../../../app")
|
||||||
|
const {
|
||||||
|
POWERUSER_LEVEL_ID,
|
||||||
|
generateAdminPermissions,
|
||||||
|
} = require("../../../utilities/accessLevels")
|
||||||
|
|
||||||
const TEST_CLIENT_ID = "test-client-id"
|
const TEST_CLIENT_ID = "test-client-id"
|
||||||
|
|
||||||
|
@ -17,7 +21,7 @@ exports.supertest = async () => {
|
||||||
|
|
||||||
exports.defaultHeaders = {
|
exports.defaultHeaders = {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
Authorization: "Basic test-admin-secret",
|
Cookie: ["builder:token=test-admin-secret"],
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createModel = async (request, instanceId, model) => {
|
exports.createModel = async (request, instanceId, model) => {
|
||||||
|
@ -37,6 +41,31 @@ exports.createModel = async (request, instanceId, model) => {
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.createRecord = async (request, instanceId, modelId, record) => {
|
||||||
|
record = record || {
|
||||||
|
modelId,
|
||||||
|
name: "test name",
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/${instanceId}/${modelId}/records`)
|
||||||
|
.send(record)
|
||||||
|
.set(exports.defaultHeaders)
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createView = async (request, instanceId, view) => {
|
||||||
|
view = view || {
|
||||||
|
map: "function(doc) { emit(doc[doc.key], doc._id); } ",
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/${instanceId}/views`)
|
||||||
|
.set(exports.defaultHeaders)
|
||||||
|
.send(view)
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
exports.createClientDatabase = async () => await create(TEST_CLIENT_ID)
|
exports.createClientDatabase = async () => await create(TEST_CLIENT_ID)
|
||||||
|
|
||||||
exports.createApplication = async (request, name = "test_application") => {
|
exports.createApplication = async (request, name = "test_application") => {
|
||||||
|
@ -64,21 +93,170 @@ exports.createInstance = async (request, appId) => {
|
||||||
exports.createUser = async (
|
exports.createUser = async (
|
||||||
request,
|
request,
|
||||||
instanceId,
|
instanceId,
|
||||||
username = "bill",
|
username = "babs",
|
||||||
password = "bills_password"
|
password = "babs_password"
|
||||||
) => {
|
) => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/${instanceId}/users`)
|
.post(`/api/${instanceId}/users`)
|
||||||
.set(exports.defaultHeaders)
|
.set(exports.defaultHeaders)
|
||||||
.send({ name: "Bill", username, password })
|
.send({
|
||||||
|
name: "Bill",
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
accessLevelId: POWERUSER_LEVEL_ID,
|
||||||
|
})
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createUser_WithOnePermission = async (
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permName,
|
||||||
|
itemId
|
||||||
|
) => {
|
||||||
|
let permissions = await generateAdminPermissions(instanceId)
|
||||||
|
permissions = permissions.filter(
|
||||||
|
p => p.name === permName && p.itemId === itemId
|
||||||
|
)
|
||||||
|
|
||||||
|
return await createUserWithPermissions(
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permissions,
|
||||||
|
"onePermOnlyUser"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createUser_WithAdminPermissions = async (request, instanceId) => {
|
||||||
|
let permissions = await generateAdminPermissions(instanceId)
|
||||||
|
|
||||||
|
return await createUserWithPermissions(
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permissions,
|
||||||
|
"adminUser"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createUser_WithAllPermissionExceptOne = async (
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permName,
|
||||||
|
itemId
|
||||||
|
) => {
|
||||||
|
let permissions = await generateAdminPermissions(instanceId)
|
||||||
|
permissions = permissions.filter(
|
||||||
|
p => !(p.name === permName && p.itemId === itemId)
|
||||||
|
)
|
||||||
|
|
||||||
|
return await createUserWithPermissions(
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permissions,
|
||||||
|
"allPermsExceptOneUser"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createUserWithPermissions = async (
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permissions,
|
||||||
|
username
|
||||||
|
) => {
|
||||||
|
const accessRes = await request
|
||||||
|
.post(`/api/${instanceId}/accesslevels`)
|
||||||
|
.send({ name: "TestLevel", permissions })
|
||||||
|
.set(exports.defaultHeaders)
|
||||||
|
|
||||||
|
const password = `password_${username}`
|
||||||
|
await request
|
||||||
|
.post(`/api/${instanceId}/users`)
|
||||||
|
.set(exports.defaultHeaders)
|
||||||
|
.send({
|
||||||
|
name: username,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
accessLevelId: accessRes.body._id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const db = new CouchDB(instanceId)
|
||||||
|
const designDoc = await db.get("_design/database")
|
||||||
|
|
||||||
|
const loginResult = await request
|
||||||
|
.post(`/api/authenticate`)
|
||||||
|
.set("Referer", `http://localhost:4001/${designDoc.metadata.applicationId}`)
|
||||||
|
.send({ username, password })
|
||||||
|
|
||||||
|
// returning necessary request headers
|
||||||
|
return {
|
||||||
|
Accept: "application/json",
|
||||||
|
Cookie: loginResult.headers["set-cookie"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.testPermissionsForEndpoint = async ({
|
||||||
|
request,
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
body,
|
||||||
|
instanceId,
|
||||||
|
permissionName,
|
||||||
|
itemId,
|
||||||
|
}) => {
|
||||||
|
const headers = await createUser_WithOnePermission(
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permissionName,
|
||||||
|
itemId
|
||||||
|
)
|
||||||
|
|
||||||
|
await createRequest(request, method, url, body)
|
||||||
|
.set(headers)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
|
const noPermsHeaders = await createUser_WithAllPermissionExceptOne(
|
||||||
|
request,
|
||||||
|
instanceId,
|
||||||
|
permissionName,
|
||||||
|
itemId
|
||||||
|
)
|
||||||
|
|
||||||
|
await createRequest(request, method, url, body)
|
||||||
|
.set(noPermsHeaders)
|
||||||
|
.expect(403)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.builderEndpointShouldBlockNormalUsers = async ({
|
||||||
|
request,
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
body,
|
||||||
|
instanceId,
|
||||||
|
}) => {
|
||||||
|
const headers = await createUser_WithAdminPermissions(request, instanceId)
|
||||||
|
|
||||||
|
await createRequest(request, method, url, body)
|
||||||
|
.set(headers)
|
||||||
|
.expect(403)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createRequest = (request, method, url, body) => {
|
||||||
|
let req
|
||||||
|
|
||||||
|
if (method === "POST") req = request.post(url).send(body)
|
||||||
|
else if (method === "GET") req = request.get(url)
|
||||||
|
else if (method === "DELETE") req = request.delete(url)
|
||||||
|
else if (method === "PATCH") req = request.patch(url).send(body)
|
||||||
|
else if (method === "PUT") req = request.put(url).send(body)
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
exports.insertDocument = async (databaseId, document) => {
|
exports.insertDocument = async (databaseId, document) => {
|
||||||
const { id, ...documentFields } = document
|
const { id, ...documentFields } = document
|
||||||
return await new CouchDB(databaseId).put({ _id: id, ...documentFields })
|
return await new CouchDB(databaseId).put({ _id: id, ...documentFields })
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroyDocument = async (databaseId, documentId) => {
|
exports.destroyDocument = async (databaseId, documentId) => {
|
||||||
return await new CouchDB(databaseId).destroy(documentId);
|
return await new CouchDB(databaseId).destroy(documentId)
|
||||||
}
|
}
|
|
@ -3,7 +3,9 @@ const {
|
||||||
createModel,
|
createModel,
|
||||||
supertest,
|
supertest,
|
||||||
createClientDatabase,
|
createClientDatabase,
|
||||||
createApplication
|
createApplication ,
|
||||||
|
defaultHeaders,
|
||||||
|
builderEndpointShouldBlockNormalUsers
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
|
|
||||||
describe("/models", () => {
|
describe("/models", () => {
|
||||||
|
@ -38,7 +40,7 @@ describe("/models", () => {
|
||||||
name: { type: "string" }
|
name: { type: "string" }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(async (err, res) => {
|
.end(async (err, res) => {
|
||||||
|
@ -47,6 +49,22 @@ describe("/models", () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/${instance._id}/models`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
body: {
|
||||||
|
name: "TestModel",
|
||||||
|
key: "name",
|
||||||
|
schema: {
|
||||||
|
name: { type: "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
|
@ -60,7 +78,7 @@ describe("/models", () => {
|
||||||
it("returns all the models for that instance in the response body", done => {
|
it("returns all the models for that instance in the response body", done => {
|
||||||
request
|
request
|
||||||
.get(`/api/${instance._id}/models`)
|
.get(`/api/${instance._id}/models`)
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(async (_, res) => {
|
.end(async (_, res) => {
|
||||||
|
@ -70,6 +88,16 @@ describe("/models", () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/models`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
|
@ -83,7 +111,7 @@ describe("/models", () => {
|
||||||
it("returns a success response when a model is deleted.", done => {
|
it("returns a success response when a model is deleted.", done => {
|
||||||
request
|
request
|
||||||
.delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`)
|
.delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`)
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(async (_, res) => {
|
.end(async (_, res) => {
|
||||||
|
@ -91,5 +119,15 @@ describe("/models", () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api/${instance._id}/models/${testModel._id}/${testModel._rev}`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,8 +3,15 @@ const {
|
||||||
createClientDatabase,
|
createClientDatabase,
|
||||||
createInstance,
|
createInstance,
|
||||||
createModel,
|
createModel,
|
||||||
supertest
|
supertest,
|
||||||
|
defaultHeaders,
|
||||||
|
testPermissionsForEndpoint,
|
||||||
|
shouldReturn403WhenNoPermission,
|
||||||
|
shouldReturn200WithOnlyOnePermission,
|
||||||
} = require("./couchTestUtils");
|
} = require("./couchTestUtils");
|
||||||
|
const {
|
||||||
|
WRITE_MODEL, READ_MODEL
|
||||||
|
} = require("../../../utilities/accessLevels")
|
||||||
|
|
||||||
describe("/records", () => {
|
describe("/records", () => {
|
||||||
let request
|
let request
|
||||||
|
@ -24,8 +31,6 @@ describe("/records", () => {
|
||||||
server.close();
|
server.close();
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("save, load, update, delete", () => {
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
instance = await createInstance(request, app._id)
|
instance = await createInstance(request, app._id)
|
||||||
model = await createModel(request, instance._id)
|
model = await createModel(request, instance._id)
|
||||||
|
@ -38,12 +43,14 @@ describe("/records", () => {
|
||||||
|
|
||||||
const createRecord = async r =>
|
const createRecord = async r =>
|
||||||
await request
|
await request
|
||||||
.post(`/api/${instance._id}/records`)
|
.post(`/api/${instance._id}/${model._id}/records`)
|
||||||
.send(r || record)
|
.send(r || record)
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
|
describe("save", () => {
|
||||||
|
|
||||||
it("returns a success message when the record is created", async () => {
|
it("returns a success message when the record is created", async () => {
|
||||||
const res = await createRecord()
|
const res = await createRecord()
|
||||||
expect(res.res.statusMessage).toEqual(`${model.name} created successfully`)
|
expect(res.res.statusMessage).toEqual(`${model.name} created successfully`)
|
||||||
|
@ -51,33 +58,47 @@ describe("/records", () => {
|
||||||
expect(res.body._rev).toBeDefined()
|
expect(res.body._rev).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await testPermissionsForEndpoint({
|
||||||
|
request,
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/${instance._id}/${model._id}/records`,
|
||||||
|
body: record,
|
||||||
|
instanceId: instance._id,
|
||||||
|
permissionName: WRITE_MODEL,
|
||||||
|
itemId: model._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("updates a record successfully", async () => {
|
it("updates a record successfully", async () => {
|
||||||
const rec = await createRecord()
|
const rec = await createRecord()
|
||||||
const existing = rec.body
|
const existing = rec.body
|
||||||
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/${instance._id}/records`)
|
.post(`/api/${instance._id}/${model._id}/records`)
|
||||||
.send({
|
.send({
|
||||||
_id: existing._id,
|
_id: existing._id,
|
||||||
_rev: existing._rev,
|
_rev: existing._rev,
|
||||||
modelId: model._id,
|
modelId: model._id,
|
||||||
name: "Updated Name",
|
name: "Updated Name",
|
||||||
})
|
})
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual(`${model.name} updated successfully.`)
|
expect(res.res.statusMessage).toEqual(`${model.name} updated successfully.`)
|
||||||
expect(res.body.name).toEqual("Updated Name")
|
expect(res.body.name).toEqual("Updated Name")
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("find", () => {
|
||||||
it("should load a record", async () => {
|
it("should load a record", async () => {
|
||||||
const rec = await createRecord()
|
const rec = await createRecord()
|
||||||
const existing = rec.body
|
const existing = rec.body
|
||||||
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/${instance._id}/records/${existing._id}`)
|
.get(`/api/${instance._id}/${model._id}/records/${existing._id}`)
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
|
@ -89,6 +110,31 @@ describe("/records", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("load should return 404 when record does not exist", async () => {
|
||||||
|
await createRecord()
|
||||||
|
await request
|
||||||
|
.get(`/api/${instance._id}/${model._id}/records/not-a-valid-id`)
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(404)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
const rec = await createRecord()
|
||||||
|
await testPermissionsForEndpoint({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/${model._id}/records/${rec.body._id}`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
permissionName: READ_MODEL,
|
||||||
|
itemId: model._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("fetch", () => {
|
||||||
|
|
||||||
it("should list all records for given modelId", async () => {
|
it("should list all records for given modelId", async () => {
|
||||||
const newRecord = {
|
const newRecord = {
|
||||||
modelId: model._id,
|
modelId: model._id,
|
||||||
|
@ -99,8 +145,8 @@ describe("/records", () => {
|
||||||
await createRecord(newRecord)
|
await createRecord(newRecord)
|
||||||
|
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/${instance._id}/all_${newRecord.modelId}/records`)
|
.get(`/api/${instance._id}/${model._id}/records`)
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
|
@ -109,13 +155,46 @@ describe("/records", () => {
|
||||||
expect(res.body.find(r => r.name === record.name)).toBeDefined()
|
expect(res.body.find(r => r.name === record.name)).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("load should return 404 when record does not exist", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
await createRecord()
|
await testPermissionsForEndpoint({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/${model._id}/records`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
permissionName: READ_MODEL,
|
||||||
|
itemId: model._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("delete", () => {
|
||||||
|
|
||||||
|
it("should remove a record", async () => {
|
||||||
|
const createRes = await createRecord()
|
||||||
|
|
||||||
await request
|
await request
|
||||||
.get(`/api/${instance._id}/records/not-a-valid-id`)
|
.delete(`/api/${instance._id}/${model._id}/records/${createRes.body._id}/${createRes.body._rev}`)
|
||||||
.set("Accept", "application/json")
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect(200)
|
||||||
|
|
||||||
|
await request
|
||||||
|
.get(`/api/${instance._id}/${model._id}/records/${createRes.body._id}`)
|
||||||
|
.set(defaultHeaders)
|
||||||
.expect(404)
|
.expect(404)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
const createRes = await createRecord()
|
||||||
|
await testPermissionsForEndpoint({
|
||||||
|
request,
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api/${instance._id}/${model._id}/records/${createRes.body._id}/${createRes.body._rev}`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
permissionName: WRITE_MODEL,
|
||||||
|
itemId: model._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,7 +5,13 @@ const {
|
||||||
supertest,
|
supertest,
|
||||||
defaultHeaders,
|
defaultHeaders,
|
||||||
createUser,
|
createUser,
|
||||||
|
testPermissionsForEndpoint,
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
|
const {
|
||||||
|
POWERUSER_LEVEL_ID,
|
||||||
|
LIST_USERS,
|
||||||
|
USER_MANAGEMENT
|
||||||
|
} = require("../../../utilities/accessLevels")
|
||||||
|
|
||||||
describe("/users", () => {
|
describe("/users", () => {
|
||||||
let request
|
let request
|
||||||
|
@ -43,6 +49,17 @@ describe("/users", () => {
|
||||||
expect(res.body.find(u => u.username === "pam")).toBeDefined()
|
expect(res.body.find(u => u.username === "pam")).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await createUser(request, instance._id, "brenda", "brendas_password")
|
||||||
|
await testPermissionsForEndpoint({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/users`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
permissionName: LIST_USERS,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
|
@ -51,12 +68,24 @@ describe("/users", () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/${instance._id}/users`)
|
.post(`/api/${instance._id}/users`)
|
||||||
.set(defaultHeaders)
|
.set(defaultHeaders)
|
||||||
.send({ name: "Bill", username: "bill", password: "bills_password" })
|
.send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: POWERUSER_LEVEL_ID })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual("User created successfully.");
|
expect(res.res.statusMessage).toEqual("User created successfully.");
|
||||||
expect(res.body._id).toBeUndefined()
|
expect(res.body._id).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await testPermissionsForEndpoint({
|
||||||
|
request,
|
||||||
|
method: "POST",
|
||||||
|
body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", accessLevelId: POWERUSER_LEVEL_ID },
|
||||||
|
url: `/api/${instance._id}/users`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
permissionName: USER_MANAGEMENT,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,9 +3,13 @@ const {
|
||||||
createApplication,
|
createApplication,
|
||||||
createInstance,
|
createInstance,
|
||||||
createModel,
|
createModel,
|
||||||
|
createRecord,
|
||||||
supertest,
|
supertest,
|
||||||
defaultHeaders
|
defaultHeaders,
|
||||||
|
testPermissionsForEndpoint,
|
||||||
|
builderEndpointShouldBlockNormalUsers,
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
|
const { READ_VIEW } = require("../../../utilities/accessLevels")
|
||||||
|
|
||||||
describe("/views", () => {
|
describe("/views", () => {
|
||||||
let request
|
let request
|
||||||
|
@ -34,11 +38,10 @@ describe("/views", () => {
|
||||||
.send({
|
.send({
|
||||||
name: "TestView",
|
name: "TestView",
|
||||||
map: `function(doc) {
|
map: `function(doc) {
|
||||||
if (doc.id) {
|
if (doc.type === 'record') {
|
||||||
emit(doc.name, doc._id);
|
emit(doc.name, doc._id);
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
reduce: `function(keys, values) { }`
|
|
||||||
})
|
})
|
||||||
.set(defaultHeaders)
|
.set(defaultHeaders)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -51,14 +54,28 @@ describe("/views", () => {
|
||||||
expect(res.res.statusMessage).toEqual("View TestView created successfully.");
|
expect(res.res.statusMessage).toEqual("View TestView created successfully.");
|
||||||
expect(res.body.name).toEqual("TestView");
|
expect(res.body.name).toEqual("TestView");
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/${instance._id}/views`,
|
||||||
|
body: {
|
||||||
|
name: "TestView",
|
||||||
|
map: `function(doc) {
|
||||||
|
if (doc.id) {
|
||||||
|
emit(doc.name, doc._id);
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
reduce: `function(keys, values) { }`
|
||||||
|
},
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
model = await createModel(request, instance._id);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should only return custom views", async () => {
|
it("should only return custom views", async () => {
|
||||||
const view = await createView()
|
const view = await createView()
|
||||||
const res = await request
|
const res = await request
|
||||||
|
@ -69,5 +86,46 @@ describe("/views", () => {
|
||||||
expect(res.body.length).toBe(1)
|
expect(res.body.length).toBe(1)
|
||||||
expect(res.body.find(v => v.name === view.body.name)).toBeDefined()
|
expect(res.body.find(v => v.name === view.body.name)).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/views`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("query", () => {
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
model = await createModel(request, instance._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return records from custom view", async () => {
|
||||||
|
await createView()
|
||||||
|
const rec1 = await createRecord(request, instance._id, model._id)
|
||||||
|
await createRecord(request, instance._id, model._id)
|
||||||
|
const res = await request
|
||||||
|
.get(`/api/${instance._id}/views/TestView`)
|
||||||
|
.set(defaultHeaders)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body.length).toBe(2)
|
||||||
|
expect(res.body.find(r => r._id === rec1._id)).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await createView()
|
||||||
|
await testPermissionsForEndpoint({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/views/TestView`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
permissionName: READ_VIEW,
|
||||||
|
itemId: "TestView",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,8 @@ const {
|
||||||
defaultHeaders,
|
defaultHeaders,
|
||||||
supertest,
|
supertest,
|
||||||
insertDocument,
|
insertDocument,
|
||||||
destroyDocument
|
destroyDocument,
|
||||||
|
builderEndpointShouldBlockNormalUsers
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
|
|
||||||
const TEST_WORKFLOW = {
|
const TEST_WORKFLOW = {
|
||||||
|
@ -71,6 +72,16 @@ describe("/workflows", () => {
|
||||||
expect(res.body.message).toEqual("Workflow created successfully");
|
expect(res.body.message).toEqual("Workflow created successfully");
|
||||||
expect(res.body.workflow.name).toEqual("My Workflow");
|
expect(res.body.workflow.name).toEqual("My Workflow");
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/${instance._id}/workflows`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
body: TEST_WORKFLOW
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
|
@ -103,6 +114,15 @@ describe("/workflows", () => {
|
||||||
|
|
||||||
expect(res.body[0]).toEqual(expect.objectContaining(TEST_WORKFLOW));
|
expect(res.body[0]).toEqual(expect.objectContaining(TEST_WORKFLOW));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/workflows`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("find", () => {
|
describe("find", () => {
|
||||||
|
@ -116,6 +136,16 @@ describe("/workflows", () => {
|
||||||
|
|
||||||
expect(res.body).toEqual(expect.objectContaining(TEST_WORKFLOW));
|
expect(res.body).toEqual(expect.objectContaining(TEST_WORKFLOW));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await createWorkflow();
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/${instance._id}/workflows/${workflow.id}`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
|
@ -129,5 +159,15 @@ describe("/workflows", () => {
|
||||||
|
|
||||||
expect(res.body.id).toEqual(TEST_WORKFLOW._id);
|
expect(res.body.id).toEqual(TEST_WORKFLOW._id);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await createWorkflow();
|
||||||
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
|
request,
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api/${instance._id}/workflows/${workflow.id}/${workflow._rev}`,
|
||||||
|
instanceId: instance._id,
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,26 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/user")
|
const controller = require("../controllers/user")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { USER_MANAGEMENT, LIST_USERS } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/:instanceId/users", controller.fetch)
|
.get("/api/:instanceId/users", authorized(LIST_USERS), controller.fetch)
|
||||||
.get("/api/:instanceId/users/:username", controller.find)
|
.get(
|
||||||
.post("/api/:instanceId/users", controller.create)
|
"/api/:instanceId/users/:username",
|
||||||
.delete("/api/:instanceId/users/:username", controller.destroy)
|
authorized(USER_MANAGEMENT),
|
||||||
|
controller.find
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/:instanceId/users",
|
||||||
|
authorized(USER_MANAGEMENT),
|
||||||
|
controller.create
|
||||||
|
)
|
||||||
|
.delete(
|
||||||
|
"/api/:instanceId/users/:username",
|
||||||
|
authorized(USER_MANAGEMENT),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/view")
|
const viewController = require("../controllers/view")
|
||||||
|
const recordController = require("../controllers/record")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER, READ_VIEW } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/:instanceId/views", controller.fetch)
|
.get(
|
||||||
|
"/api/:instanceId/views/:viewName",
|
||||||
|
authorized(READ_VIEW, ctx => ctx.params.viewName),
|
||||||
|
recordController.fetchView
|
||||||
|
)
|
||||||
|
.get("/api/:instanceId/views", authorized(BUILDER), viewController.fetch)
|
||||||
// .patch("/api/:databaseId/views", controller.update);
|
// .patch("/api/:databaseId/views", controller.update);
|
||||||
// .delete("/api/:instanceId/views/:viewId/:revId", controller.destroy);
|
// .delete("/api/:instanceId/views/:viewId/:revId", controller.destroy);
|
||||||
.post("/api/:instanceId/views", controller.create)
|
.post("/api/:instanceId/views", authorized(BUILDER), viewController.create)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,15 +1,28 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/workflow")
|
const controller = require("../controllers/workflow")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/accessLevels")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/:instanceId/workflows", controller.fetch)
|
.get("/api/:instanceId/workflows", authorized(BUILDER), controller.fetch)
|
||||||
.get("/api/:instanceId/workflows/:id", controller.find)
|
.get("/api/:instanceId/workflows/:id", authorized(BUILDER), controller.find)
|
||||||
.get("/api/:instanceId/workflows/:id/:action", controller.fetchActionScript)
|
.get(
|
||||||
.post("/api/:instanceId/workflows", controller.create)
|
"/api/:instanceId/workflows/:id/:action",
|
||||||
.post("/api/:instanceId/workflows/action", controller.executeAction)
|
authorized(BUILDER),
|
||||||
.put("/api/:instanceId/workflows", controller.update)
|
controller.fetchActionScript
|
||||||
.delete("/api/:instanceId/workflows/:id/:rev", controller.destroy)
|
)
|
||||||
|
.put("/api/:instanceId/workflows", authorized(BUILDER), controller.update)
|
||||||
|
.post(
|
||||||
|
"/api/:instanceId/workflows/action",
|
||||||
|
authorized(BUILDER),
|
||||||
|
controller.executeAction
|
||||||
|
)
|
||||||
|
.delete(
|
||||||
|
"/api/:instanceId/workflows/:id",
|
||||||
|
authorized(BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const STATUS_CODES = require("../utilities/statusCodes")
|
const STATUS_CODES = require("../utilities/statusCodes")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
|
const accessLevelController = require("../api/controllers/accesslevel")
|
||||||
|
const {
|
||||||
|
ADMIN_LEVEL_ID,
|
||||||
|
POWERUSER_LEVEL_ID,
|
||||||
|
} = require("../utilities/accessLevels")
|
||||||
|
|
||||||
module.exports = async (ctx, next) => {
|
module.exports = async (ctx, next) => {
|
||||||
if (ctx.path === "/_builder") {
|
if (ctx.path === "/_builder") {
|
||||||
|
@ -8,8 +13,9 @@ module.exports = async (ctx, next) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.isDev && ctx.cookies.get("builder:token") === env.ADMIN_SECRET) {
|
if (ctx.cookies.get("builder:token") === env.ADMIN_SECRET) {
|
||||||
ctx.isAuthenticated = true
|
ctx.isAuthenticated = true
|
||||||
|
ctx.isBuilder = true
|
||||||
await next()
|
await next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -23,7 +29,15 @@ module.exports = async (ctx, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ctx.jwtPayload = jwt.verify(token, ctx.config.jwtSecret)
|
const jwtPayload = jwt.verify(token, ctx.config.jwtSecret)
|
||||||
|
|
||||||
|
ctx.user = {
|
||||||
|
...jwtPayload,
|
||||||
|
accessLevel: await getAccessLevel(
|
||||||
|
jwtPayload.instanceId,
|
||||||
|
jwtPayload.accessLevelId
|
||||||
|
),
|
||||||
|
}
|
||||||
ctx.isAuthenticated = true
|
ctx.isAuthenticated = true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
|
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
|
||||||
|
@ -31,3 +45,25 @@ module.exports = async (ctx, next) => {
|
||||||
|
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAccessLevel = async (instanceId, accessLevelId) => {
|
||||||
|
if (
|
||||||
|
accessLevelId === POWERUSER_LEVEL_ID ||
|
||||||
|
accessLevelId === ADMIN_LEVEL_ID
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
_id: accessLevelId,
|
||||||
|
name: accessLevelId,
|
||||||
|
permissions: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const findAccessContext = {
|
||||||
|
params: {
|
||||||
|
levelId: accessLevelId,
|
||||||
|
instanceId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await accessLevelController.find(findAccessContext)
|
||||||
|
return findAccessContext.body
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
const {
|
||||||
|
adminPermissions,
|
||||||
|
ADMIN_LEVEL_ID,
|
||||||
|
POWERUSER_LEVEL_ID,
|
||||||
|
BUILDER,
|
||||||
|
} = require("../utilities/accessLevels")
|
||||||
|
|
||||||
|
module.exports = (permName, getItemId) => async (ctx, next) => {
|
||||||
|
if (!ctx.isAuthenticated) {
|
||||||
|
ctx.throw(403, "Session not authenticated")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.isBuilder) {
|
||||||
|
await next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permName === BUILDER) {
|
||||||
|
ctx.throw(403, "Not Authorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.user) {
|
||||||
|
ctx.throw(403, "User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
|
||||||
|
|
||||||
|
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
|
||||||
|
await next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisPermissionId = permissionId({
|
||||||
|
name: permName,
|
||||||
|
itemId: getItemId && getItemId(ctx),
|
||||||
|
})
|
||||||
|
|
||||||
|
// power user has everything, except the admin specific perms
|
||||||
|
if (
|
||||||
|
ctx.user.accessLevel._id === POWERUSER_LEVEL_ID &&
|
||||||
|
!adminPermissions.map(permissionId).includes(thisPermissionId)
|
||||||
|
) {
|
||||||
|
await next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
ctx.user.accessLevel.permissions
|
||||||
|
.map(permissionId)
|
||||||
|
.includes(thisPermissionId)
|
||||||
|
) {
|
||||||
|
await next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.throw(403, "Not Authorized")
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
const viewController = require("../api/controllers/view")
|
||||||
|
const modelController = require("../api/controllers/model")
|
||||||
|
const workflowController = require("../api/controllers/workflow")
|
||||||
|
|
||||||
|
exports.ADMIN_LEVEL_ID = "ADMIN"
|
||||||
|
exports.POWERUSER_LEVEL_ID = "POWER_USER"
|
||||||
|
|
||||||
|
exports.READ_MODEL = "read-model"
|
||||||
|
exports.WRITE_MODEL = "write-model"
|
||||||
|
exports.READ_VIEW = "read-view"
|
||||||
|
exports.EXECUTE_WORKFLOW = "execute-workflow"
|
||||||
|
exports.USER_MANAGEMENT = "user-management"
|
||||||
|
exports.BUILDER = "builder"
|
||||||
|
exports.LIST_USERS = "list-users"
|
||||||
|
|
||||||
|
exports.adminPermissions = [
|
||||||
|
{
|
||||||
|
name: exports.USER_MANAGEMENT,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
exports.generateAdminPermissions = async instanceId => [
|
||||||
|
...exports.adminPermissions,
|
||||||
|
...(await exports.generatePowerUserPermissions(instanceId)),
|
||||||
|
]
|
||||||
|
|
||||||
|
exports.generatePowerUserPermissions = async instanceId => {
|
||||||
|
const fetchModelsCtx = {
|
||||||
|
params: {
|
||||||
|
instanceId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await modelController.fetch(fetchModelsCtx)
|
||||||
|
const models = fetchModelsCtx.body
|
||||||
|
|
||||||
|
const fetchViewsCtx = {
|
||||||
|
params: {
|
||||||
|
instanceId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await viewController.fetch(fetchViewsCtx)
|
||||||
|
const views = fetchViewsCtx.body
|
||||||
|
|
||||||
|
const fetchWorkflowsCtx = {
|
||||||
|
params: {
|
||||||
|
instanceId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await workflowController.fetch(fetchWorkflowsCtx)
|
||||||
|
const workflows = fetchWorkflowsCtx.body
|
||||||
|
|
||||||
|
const readModelPermissions = models.map(m => ({
|
||||||
|
itemId: m._id,
|
||||||
|
name: exports.READ_MODEL,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const writeModelPermissions = models.map(m => ({
|
||||||
|
itemId: m._id,
|
||||||
|
name: exports.WRITE_MODEL,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const viewPermissions = views.map(v => ({
|
||||||
|
itemId: v.name,
|
||||||
|
name: exports.READ_VIEW,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const executeWorkflowPermissions = workflows.map(w => ({
|
||||||
|
itemId: w._id,
|
||||||
|
name: exports.EXECUTE_WORKFLOW,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return [
|
||||||
|
...readModelPermissions,
|
||||||
|
...writeModelPermissions,
|
||||||
|
...viewPermissions,
|
||||||
|
...executeWorkflowPermissions,
|
||||||
|
{ name: exports.LIST_USERS },
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue