Merge pull request #1383 from Budibase/feature/global-user-management
Feature/global user management
This commit is contained in:
commit
a1bcb2f857
|
@ -0,0 +1 @@
|
|||
packages/server/builder/**/*.js
|
|
@ -35,9 +35,10 @@ services:
|
|||
environment:
|
||||
SELF_HOSTED: 1
|
||||
PORT: 4003
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
|
||||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
|
||||
RAW_MINIO_URL: http://minio-service:9000
|
||||
MINIO_URL: http://minio-service:9000
|
||||
COUCH_DB_USERNAME: ${COUCH_DB_USER}
|
||||
COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD}
|
||||
COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984
|
||||
|
|
|
@ -26,6 +26,10 @@ static_resources:
|
|||
cluster: redis-service
|
||||
prefix_rewrite: "/"
|
||||
|
||||
- match: { prefix: "/api/admin/" }
|
||||
route:
|
||||
cluster: worker-dev
|
||||
|
||||
- match: { prefix: "/api/" }
|
||||
route:
|
||||
cluster: server-dev
|
||||
|
@ -43,6 +47,10 @@ static_resources:
|
|||
cluster: builder-dev
|
||||
prefix_rewrite: "/builder/"
|
||||
|
||||
# special case in dev to redirect no path to builder
|
||||
- match: { path: "/" }
|
||||
redirect: { path_redirect: "/builder/" }
|
||||
|
||||
# minio is on the default route because this works
|
||||
# best, minio + AWS SDK doesn't handle path proxy
|
||||
- match: { prefix: "/" }
|
||||
|
@ -123,3 +131,17 @@ static_resources:
|
|||
address: {{ address }}
|
||||
port_value: 3000
|
||||
|
||||
- name: worker-dev
|
||||
connect_timeout: 0.25s
|
||||
type: strict_dns
|
||||
lb_policy: round_robin
|
||||
load_assignment:
|
||||
cluster_name: worker-dev
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: {{ address }}
|
||||
port_value: 4002
|
||||
|
||||
|
|
|
@ -26,6 +26,11 @@ static_resources:
|
|||
route:
|
||||
cluster: app-service
|
||||
|
||||
# special case for worker admin API
|
||||
- match: { path: "/api/admin" }
|
||||
route:
|
||||
cluster: worker-service
|
||||
|
||||
# special case for when API requests are made, can just forward, not to minio
|
||||
- match: { prefix: "/api/" }
|
||||
route:
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
"clean": "lerna clean",
|
||||
"kill-port": "kill-port 4001",
|
||||
"dev": "yarn run kill-port && lerna link && lerna run --parallel dev:builder --concurrency 1",
|
||||
"dev:noserver": "lerna link && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/server",
|
||||
"dev:noserver": "lerna link && lerna run dev:stack:up && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/server --ignore @budibase/worker",
|
||||
"test": "lerna run test",
|
||||
"lint": "eslint packages",
|
||||
"lint:fix": "eslint --fix packages",
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
|
@ -0,0 +1 @@
|
|||
# Budibase Authentication Library
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "@budibase/auth",
|
||||
"version": "0.0.1",
|
||||
"description": "Authentication middlewares for budibase builder and apps",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"koa-passport": "^4.1.4",
|
||||
"passport-google-auth": "^1.0.2",
|
||||
"passport-google-oauth": "^2.0.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-local": "^1.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
exports.UserStatus = {
|
||||
ACTIVE: "active",
|
||||
INACTIVE: "inactive",
|
||||
}
|
||||
|
||||
exports.Cookies = {
|
||||
CurrentApp: "budibase:currentapp",
|
||||
Auth: "budibase:auth",
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module.exports.setDB = pouch => {
|
||||
module.exports.CouchDB = pouch
|
||||
}
|
||||
|
||||
module.exports.CouchDB = null
|
|
@ -0,0 +1,44 @@
|
|||
exports.StaticDatabases = {
|
||||
USER: {
|
||||
name: "user-db",
|
||||
},
|
||||
}
|
||||
|
||||
const DocumentTypes = {
|
||||
USER: "us",
|
||||
APP: "app",
|
||||
}
|
||||
|
||||
exports.DocumentTypes = DocumentTypes
|
||||
|
||||
const UNICODE_MAX = "\ufff0"
|
||||
const SEPARATOR = "_"
|
||||
|
||||
exports.SEPARATOR = SEPARATOR
|
||||
|
||||
/**
|
||||
* Generates a new user ID based on the passed in email.
|
||||
* @param {string} email The email which the ID is going to be built up of.
|
||||
* @returns {string} The new user ID which the user doc can be stored under.
|
||||
*/
|
||||
exports.generateUserID = email => {
|
||||
return `${DocumentTypes.USER}${SEPARATOR}${email}`
|
||||
}
|
||||
|
||||
exports.getEmailFromUserID = userId => {
|
||||
return userId.split(`${DocumentTypes.USER}${SEPARATOR}`)[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
||||
*/
|
||||
exports.getUserParams = (email = "", otherProps = {}) => {
|
||||
if (!email) {
|
||||
email = ""
|
||||
}
|
||||
return {
|
||||
...otherProps,
|
||||
startkey: `${DocumentTypes.USER}${SEPARATOR}${email}`,
|
||||
endkey: `${DocumentTypes.USER}${SEPARATOR}${email}${UNICODE_MAX}`,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
JWT_SECRET: process.env.JWT_SECRET,
|
||||
COUCH_DB_URL: process.env.COUCH_DB_URL,
|
||||
SALT_ROUNDS: process.env.SALT_ROUNDS,
|
||||
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
|
||||
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
||||
GOOGLE_AUTH_CALLBACK_URL: process.env.GOOGLE_AUTH_CALLBACK_URL,
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
const bcrypt = require("bcryptjs")
|
||||
const env = require("./environment")
|
||||
|
||||
const SALT_ROUNDS = env.SALT_ROUNDS || 10
|
||||
|
||||
exports.hash = async data => {
|
||||
const salt = await bcrypt.genSalt(SALT_ROUNDS)
|
||||
return bcrypt.hash(data, salt)
|
||||
}
|
||||
|
||||
exports.compare = async (data, encrypted) => {
|
||||
return bcrypt.compare(data, encrypted)
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
const passport = require("koa-passport")
|
||||
const LocalStrategy = require("passport-local").Strategy
|
||||
const JwtStrategy = require("passport-jwt").Strategy
|
||||
// const GoogleStrategy = require("passport-google-oauth").Strategy
|
||||
const database = require("./db")
|
||||
const { StaticDatabases } = require("./db/utils")
|
||||
const { jwt, local, authenticated } = require("./middleware")
|
||||
const { Cookies, UserStatus } = require("./constants")
|
||||
const { hash, compare } = require("./hashing")
|
||||
const {
|
||||
getAppId,
|
||||
setCookie,
|
||||
getCookie,
|
||||
clearCookie,
|
||||
isClient,
|
||||
} = require("./utils")
|
||||
const {
|
||||
generateUserID,
|
||||
getUserParams,
|
||||
getEmailFromUserID,
|
||||
} = require("./db/utils")
|
||||
|
||||
// Strategies
|
||||
passport.use(new LocalStrategy(local.options, local.authenticate))
|
||||
passport.use(new JwtStrategy(jwt.options, jwt.authenticate))
|
||||
// passport.use(new GoogleStrategy(google.options, google.authenticate))
|
||||
|
||||
passport.serializeUser((user, done) => done(null, user))
|
||||
|
||||
passport.deserializeUser(async (user, done) => {
|
||||
const db = new database.CouchDB(StaticDatabases.USER.name)
|
||||
|
||||
try {
|
||||
const user = await db.get(user._id)
|
||||
return done(null, user)
|
||||
} catch (err) {
|
||||
console.error("User not found", err)
|
||||
return done(null, false, { message: "User not found" })
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
init(pouch) {
|
||||
database.setDB(pouch)
|
||||
},
|
||||
passport,
|
||||
Cookies,
|
||||
UserStatus,
|
||||
StaticDatabases,
|
||||
generateUserID,
|
||||
getUserParams,
|
||||
getEmailFromUserID,
|
||||
hash,
|
||||
compare,
|
||||
getAppId,
|
||||
setCookie,
|
||||
getCookie,
|
||||
clearCookie,
|
||||
authenticated,
|
||||
isClient,
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
const { Cookies } = require("../constants")
|
||||
const { getCookie } = require("../utils")
|
||||
const { getEmailFromUserID } = require("../db/utils")
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
try {
|
||||
// check the actual user is authenticated first
|
||||
const authCookie = getCookie(ctx, Cookies.Auth)
|
||||
|
||||
if (authCookie) {
|
||||
ctx.isAuthenticated = true
|
||||
ctx.user = authCookie
|
||||
// make sure email is correct from ID
|
||||
ctx.user.email = getEmailFromUserID(authCookie.userId)
|
||||
}
|
||||
|
||||
await next()
|
||||
} catch (err) {
|
||||
ctx.throw(err.status || 403, err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
const jwt = require("./passport/jwt")
|
||||
const local = require("./passport/local")
|
||||
const google = require("./passport/google")
|
||||
const authenticated = require("./authenticated")
|
||||
|
||||
module.exports = {
|
||||
google,
|
||||
jwt,
|
||||
local,
|
||||
authenticated,
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
const env = require("../../environment")
|
||||
|
||||
exports.options = {
|
||||
clientId: env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
||||
callbackURL: env.GOOGLE_AUTH_CALLBACK_URL,
|
||||
}
|
||||
|
||||
// exports.authenticate = async function(token, tokenSecret, profile, done) {
|
||||
// // retrieve user ...
|
||||
// fetchUser().then(user => done(null, user))
|
||||
// }
|
|
@ -0,0 +1,17 @@
|
|||
const { Cookies } = require("../../constants")
|
||||
const env = require("../../environment")
|
||||
|
||||
exports.options = {
|
||||
secretOrKey: env.JWT_SECRET,
|
||||
jwtFromRequest: function(ctx) {
|
||||
return ctx.cookies.get(Cookies.Auth)
|
||||
},
|
||||
}
|
||||
|
||||
exports.authenticate = async function(jwt, done) {
|
||||
try {
|
||||
return done(null, jwt)
|
||||
} catch (err) {
|
||||
return done(new Error("JWT invalid."), false)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
const jwt = require("jsonwebtoken")
|
||||
const { UserStatus } = require("../../constants")
|
||||
const database = require("../../db")
|
||||
const { StaticDatabases, generateUserID } = require("../../db/utils")
|
||||
const { compare } = require("../../hashing")
|
||||
const env = require("../../environment")
|
||||
|
||||
const INVALID_ERR = "Invalid Credentials"
|
||||
|
||||
exports.options = {}
|
||||
|
||||
/**
|
||||
* Passport Local Authentication Middleware.
|
||||
* @param {*} username - username to login with
|
||||
* @param {*} password - plain text password to log in with
|
||||
* @param {*} done - callback from passport to return user information and errors
|
||||
* @returns The authenticated user, or errors if they occur
|
||||
*/
|
||||
exports.authenticate = async function(username, password, done) {
|
||||
if (!username) return done(null, false, "Email Required.")
|
||||
if (!password) return done(null, false, "Password Required.")
|
||||
|
||||
// Check the user exists in the instance DB by email
|
||||
const db = new database.CouchDB(StaticDatabases.USER.name)
|
||||
|
||||
let dbUser
|
||||
try {
|
||||
dbUser = await db.get(generateUserID(username))
|
||||
} catch (err) {
|
||||
console.error("User not found", err)
|
||||
return done(null, false, { message: "User not found" })
|
||||
}
|
||||
|
||||
// check that the user is currently inactive, if this is the case throw invalid
|
||||
if (dbUser.status === UserStatus.INACTIVE) {
|
||||
return done(null, false, { message: INVALID_ERR })
|
||||
}
|
||||
|
||||
// authenticate
|
||||
if (await compare(password, dbUser.password)) {
|
||||
const payload = {
|
||||
userId: dbUser._id,
|
||||
builder: dbUser.builder,
|
||||
email: dbUser.email,
|
||||
}
|
||||
|
||||
dbUser.token = jwt.sign(payload, env.JWT_SECRET, {
|
||||
expiresIn: "1 day",
|
||||
})
|
||||
// Remove users password in payload
|
||||
delete dbUser.password
|
||||
|
||||
return done(null, dbUser)
|
||||
} else {
|
||||
done(new Error(INVALID_ERR), false)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
const { DocumentTypes, SEPARATOR } = require("./db/utils")
|
||||
const jwt = require("jsonwebtoken")
|
||||
const { options } = require("./middleware/passport/jwt")
|
||||
|
||||
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||
|
||||
function confirmAppId(possibleAppId) {
|
||||
return possibleAppId && possibleAppId.startsWith(APP_PREFIX)
|
||||
? possibleAppId
|
||||
: undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a request tries to find the appId, which can be located in various places
|
||||
* @param {object} ctx The main request body to look through.
|
||||
* @returns {string|undefined} If an appId was found it will be returned.
|
||||
*/
|
||||
exports.getAppId = ctx => {
|
||||
const options = [ctx.headers["x-budibase-app-id"], ctx.params.appId]
|
||||
if (ctx.subdomains) {
|
||||
options.push(ctx.subdomains[1])
|
||||
}
|
||||
let appId
|
||||
for (let option of options) {
|
||||
appId = confirmAppId(option)
|
||||
if (appId) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// look in body if can't find it in subdomain
|
||||
if (!appId && ctx.request.body && ctx.request.body.appId) {
|
||||
appId = confirmAppId(ctx.request.body.appId)
|
||||
}
|
||||
let appPath =
|
||||
ctx.request.headers.referrer ||
|
||||
ctx.path.split("/").filter(subPath => subPath.startsWith(APP_PREFIX))
|
||||
if (!appId && appPath.length !== 0) {
|
||||
appId = confirmAppId(appPath[0])
|
||||
}
|
||||
return appId
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cookie from context, and decrypt if necessary.
|
||||
* @param {object} ctx The request which is to be manipulated.
|
||||
* @param {string} name The name of the cookie to get.
|
||||
*/
|
||||
exports.getCookie = (ctx, name) => {
|
||||
const cookie = ctx.cookies.get(name)
|
||||
|
||||
if (!cookie) {
|
||||
return cookie
|
||||
}
|
||||
|
||||
return jwt.verify(cookie, options.secretOrKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a cookie for the request, has a hardcoded expiry.
|
||||
* @param {object} ctx The request which is to be manipulated.
|
||||
* @param {string} name The name of the cookie to set.
|
||||
* @param {string|object} value The value of cookie which will be set.
|
||||
*/
|
||||
exports.setCookie = (ctx, value, name = "builder") => {
|
||||
const expires = new Date()
|
||||
expires.setDate(expires.getDate() + 1)
|
||||
|
||||
if (!value) {
|
||||
ctx.cookies.set(name)
|
||||
} else {
|
||||
value = jwt.sign(value, options.secretOrKey, {
|
||||
expiresIn: "1 day",
|
||||
})
|
||||
ctx.cookies.set(name, value, {
|
||||
expires,
|
||||
path: "/",
|
||||
httpOnly: false,
|
||||
overwrite: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function, simply calls setCookie with an empty string for value
|
||||
*/
|
||||
exports.clearCookie = (ctx, name) => {
|
||||
exports.setCookie(ctx, "", name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the API call being made (based on the provided ctx object) is from the client. If
|
||||
* the call is not from a client app then it is from the builder.
|
||||
* @param {object} ctx The koa context object to be tested.
|
||||
* @return {boolean} returns true if the call is from the client lib (a built app rather than the builder).
|
||||
*/
|
||||
exports.isClient = ctx => {
|
||||
return ctx.headers["x-budibase-type"] === "client"
|
||||
}
|
|
@ -0,0 +1,594 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
ajv@^6.12.3:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
asn1@~0.2.3:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
|
||||
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
|
||||
dependencies:
|
||||
safer-buffer "~2.1.0"
|
||||
|
||||
assert-plus@1.0.0, assert-plus@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
||||
|
||||
async@~2.1.4:
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
|
||||
integrity sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw=
|
||||
dependencies:
|
||||
lodash "^4.14.0"
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||
|
||||
aws-sign2@~0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
|
||||
|
||||
aws4@^1.8.0:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||
|
||||
base64url@3.x.x:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
|
||||
integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==
|
||||
|
||||
bcrypt-pbkdf@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
||||
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
|
||||
dependencies:
|
||||
tweetnacl "^0.14.3"
|
||||
|
||||
bcryptjs@^2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
|
||||
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
core-util-is@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
dashdash@^1.12.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
ecc-jsbn@~0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
|
||||
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
|
||||
dependencies:
|
||||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
extend@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||
|
||||
extsprintf@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
|
||||
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
|
||||
|
||||
extsprintf@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
||||
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
||||
forever-agent@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
||||
|
||||
form-data@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
getpass@^0.1.1:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
|
||||
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
google-auth-library@~0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e"
|
||||
integrity sha1-bhW6vuhf0d0U2NEoopW2g41SE24=
|
||||
dependencies:
|
||||
gtoken "^1.2.1"
|
||||
jws "^3.1.4"
|
||||
lodash.noop "^3.0.1"
|
||||
request "^2.74.0"
|
||||
|
||||
google-p12-pem@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177"
|
||||
integrity sha1-M8RqsCGqc0+gMys5YKmj/8svMXc=
|
||||
dependencies:
|
||||
node-forge "^0.7.1"
|
||||
|
||||
googleapis@^16.0.0:
|
||||
version "16.1.0"
|
||||
resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-16.1.0.tgz#0f19f2d70572d918881a0f626e3b1a2fa8629576"
|
||||
integrity sha1-Dxny1wVy2RiIGg9ibjsaL6hilXY=
|
||||
dependencies:
|
||||
async "~2.1.4"
|
||||
google-auth-library "~0.10.0"
|
||||
string-template "~1.0.0"
|
||||
|
||||
gtoken@^1.2.1:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8"
|
||||
integrity sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w==
|
||||
dependencies:
|
||||
google-p12-pem "^0.1.0"
|
||||
jws "^3.0.0"
|
||||
mime "^1.4.1"
|
||||
request "^2.72.0"
|
||||
|
||||
har-schema@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
|
||||
|
||||
har-validator@~5.1.3:
|
||||
version "5.1.5"
|
||||
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
|
||||
integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
|
||||
dependencies:
|
||||
ajv "^6.12.3"
|
||||
har-schema "^2.0.0"
|
||||
|
||||
http-signature@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
||||
isstream@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json-schema@0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
|
||||
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
|
||||
|
||||
json-stringify-safe@~5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
|
||||
|
||||
jsonwebtoken@^8.2.0, jsonwebtoken@^8.5.1:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
dependencies:
|
||||
jws "^3.2.2"
|
||||
lodash.includes "^4.3.0"
|
||||
lodash.isboolean "^3.0.3"
|
||||
lodash.isinteger "^4.0.4"
|
||||
lodash.isnumber "^3.0.3"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.once "^4.0.0"
|
||||
ms "^2.1.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
|
||||
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
|
||||
dependencies:
|
||||
assert-plus "1.0.0"
|
||||
extsprintf "1.3.0"
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^3.0.0, jws@^3.1.4, jws@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||
dependencies:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
koa-passport@^4.1.4:
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.4.tgz#5f1665c1c2a37ace79af9f970b770885ca30ccfa"
|
||||
integrity sha512-dJBCkl4X+zdYxbI2V2OtoGy0PUenpvp2ZLLWObc8UJhsId0iQpTFT8RVcuA0709AL2txGwRHnSPoT1bYNGa6Kg==
|
||||
dependencies:
|
||||
passport "^0.4.0"
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||
|
||||
lodash.isinteger@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||
|
||||
lodash.isnumber@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||
|
||||
lodash.noop@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c"
|
||||
integrity sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=
|
||||
|
||||
lodash.once@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash@^4.14.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
mime-db@1.47.0:
|
||||
version "1.47.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
|
||||
integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
|
||||
|
||||
mime-types@^2.1.12, mime-types@~2.1.19:
|
||||
version "2.1.30"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
|
||||
integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==
|
||||
dependencies:
|
||||
mime-db "1.47.0"
|
||||
|
||||
mime@^1.4.1:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
node-forge@^0.7.1:
|
||||
version "0.7.6"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
|
||||
integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
|
||||
|
||||
oauth-sign@~0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
||||
|
||||
oauth@0.9.x:
|
||||
version "0.9.15"
|
||||
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
|
||||
integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE=
|
||||
|
||||
passport-google-auth@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938"
|
||||
integrity sha1-izALWqRC70M94dgy7TESh30LKTg=
|
||||
dependencies:
|
||||
googleapis "^16.0.0"
|
||||
passport-strategy "1.x"
|
||||
|
||||
passport-google-oauth1@1.x.x:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz#af74a803df51ec646f66a44d82282be6f108e0cc"
|
||||
integrity sha1-r3SoA99R7GRvZqRNgigr5vEI4Mw=
|
||||
dependencies:
|
||||
passport-oauth1 "1.x.x"
|
||||
|
||||
passport-google-oauth20@2.x.x:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz#0d241b2d21ebd3dc7f2b60669ec4d587e3a674ef"
|
||||
integrity sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==
|
||||
dependencies:
|
||||
passport-oauth2 "1.x.x"
|
||||
|
||||
passport-google-oauth@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-google-oauth/-/passport-google-oauth-2.0.0.tgz#f6eb4bc96dd6c16ec0ecfdf4e05ec48ca54d4dae"
|
||||
integrity sha512-JKxZpBx6wBQXX1/a1s7VmdBgwOugohH+IxCy84aPTZNq/iIPX6u7Mqov1zY7MKRz3niFPol0KJz8zPLBoHKtYA==
|
||||
dependencies:
|
||||
passport-google-oauth1 "1.x.x"
|
||||
passport-google-oauth20 "2.x.x"
|
||||
|
||||
passport-jwt@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065"
|
||||
integrity sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==
|
||||
dependencies:
|
||||
jsonwebtoken "^8.2.0"
|
||||
passport-strategy "^1.0.0"
|
||||
|
||||
passport-local@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee"
|
||||
integrity sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-oauth1@1.x.x:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-oauth1/-/passport-oauth1-1.1.0.tgz#a7de988a211f9cf4687377130ea74df32730c918"
|
||||
integrity sha1-p96YiiEfnPRoc3cTDqdN8ycwyRg=
|
||||
dependencies:
|
||||
oauth "0.9.x"
|
||||
passport-strategy "1.x.x"
|
||||
utils-merge "1.x.x"
|
||||
|
||||
passport-oauth2@1.x.x:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.5.0.tgz#64babbb54ac46a4dcab35e7f266ed5294e3c4108"
|
||||
integrity sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==
|
||||
dependencies:
|
||||
base64url "3.x.x"
|
||||
oauth "0.9.x"
|
||||
passport-strategy "1.x.x"
|
||||
uid2 "0.0.x"
|
||||
utils-merge "1.x.x"
|
||||
|
||||
passport-strategy@1.x, passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||
integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
|
||||
|
||||
passport@^0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270"
|
||||
integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
pause "0.0.1"
|
||||
|
||||
pause@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
|
||||
integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=
|
||||
|
||||
performance-now@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
psl@^1.1.28:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
|
||||
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
|
||||
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||
|
||||
request@^2.72.0, request@^2.74.0:
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||
dependencies:
|
||||
aws-sign2 "~0.7.0"
|
||||
aws4 "^1.8.0"
|
||||
caseless "~0.12.0"
|
||||
combined-stream "~1.0.6"
|
||||
extend "~3.0.2"
|
||||
forever-agent "~0.6.1"
|
||||
form-data "~2.3.2"
|
||||
har-validator "~5.1.3"
|
||||
http-signature "~1.2.0"
|
||||
is-typedarray "~1.0.0"
|
||||
isstream "~0.1.2"
|
||||
json-stringify-safe "~5.0.1"
|
||||
mime-types "~2.1.19"
|
||||
oauth-sign "~0.9.0"
|
||||
performance-now "^2.1.0"
|
||||
qs "~6.5.2"
|
||||
safe-buffer "^5.1.2"
|
||||
tough-cookie "~2.5.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
sshpk@^1.7.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
|
||||
dependencies:
|
||||
asn1 "~0.2.3"
|
||||
assert-plus "^1.0.0"
|
||||
bcrypt-pbkdf "^1.0.0"
|
||||
dashdash "^1.12.0"
|
||||
ecc-jsbn "~0.1.1"
|
||||
getpass "^0.1.1"
|
||||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.0.2"
|
||||
tweetnacl "~0.14.0"
|
||||
|
||||
string-template@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
|
||||
integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y=
|
||||
|
||||
tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
||||
dependencies:
|
||||
psl "^1.1.28"
|
||||
punycode "^2.1.1"
|
||||
|
||||
tunnel-agent@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
|
||||
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
version "0.14.5"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||
|
||||
uid2@0.0.x:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82"
|
||||
integrity sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
|
||||
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
utils-merge@1.x.x:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
verror@1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
|
||||
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
|
@ -1,5 +1,6 @@
|
|||
context("Create an Application", () => {
|
||||
it("should create a new application", () => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.contains("Cypress Tests").should("exist")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
context("Create a automation", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
})
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
context("Create Bindings", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
cy.navigateToFrontend()
|
||||
})
|
||||
|
@ -9,7 +10,7 @@ context("Create Bindings", () => {
|
|||
addSettingBinding("text", "Current User._id")
|
||||
cy.getComponent(componentId).should(
|
||||
"have.text",
|
||||
`ro_ta_users_us_test@test.com`
|
||||
`ro_ta_users_test@test.com`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@ context("Create Components", () => {
|
|||
let headlineId
|
||||
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
cy.createTable("dog")
|
||||
cy.addColumn("dog", "name", "string")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
context("Screen Tests", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
cy.navigateToFrontend()
|
||||
})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
context("Create a Table", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
})
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
context("Create a User", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
})
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
context("Create a View", () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
cy.createTestApp()
|
||||
cy.createTable("data")
|
||||
cy.addColumn("data", "group", "Text")
|
||||
|
|
|
@ -3,13 +3,16 @@ const path = require("path")
|
|||
|
||||
const tmpdir = path.join(require("os").tmpdir(), ".budibase")
|
||||
|
||||
const WORKER_PORT = "4002"
|
||||
const MAIN_PORT = cypressConfig.env.PORT
|
||||
process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE"
|
||||
process.env.NODE_ENV = "cypress"
|
||||
process.env.ENABLE_ANALYTICS = "false"
|
||||
process.env.PORT = cypressConfig.env.PORT
|
||||
process.env.PORT = MAIN_PORT
|
||||
process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET
|
||||
process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
|
||||
process.env.SELF_HOSTED = 1
|
||||
process.env.WORKER_URL = "http://localhost:4002/"
|
||||
process.env.MINIO_URL = "http://localhost:10000/"
|
||||
process.env.MINIO_ACCESS_KEY = "budibase"
|
||||
process.env.MINIO_SECRET_KEY = "budibase"
|
||||
|
@ -25,18 +28,12 @@ async function run() {
|
|||
// dont make this a variable or top level require
|
||||
// it will cause environment module to be loaded prematurely
|
||||
const server = require("../../server/src/app")
|
||||
process.env.PORT = WORKER_PORT
|
||||
const worker = require("../../worker/src/index")
|
||||
// reload main port for rest of system
|
||||
process.env.PORT = MAIN_PORT
|
||||
server.on("close", () => console.log("Server Closed"))
|
||||
worker.on("close", () => console.log("Worker Closed"))
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
// TODO: ensure that this still works
|
||||
// initialiseBudibase({ dir: homedir, clientId: "cypress-test" })
|
||||
// .then(() => {
|
||||
// delete require.cache[require.resolve("../../server/src/environment")]
|
||||
// const xPlatHomeDir = homedir.startsWith("~")
|
||||
// ? join(homedir(), homedir.substring(1))
|
||||
// : homedir
|
||||
// run(xPlatHomeDir)
|
||||
// })
|
||||
// .catch(e => console.error(e))
|
||||
|
|
|
@ -24,6 +24,23 @@
|
|||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
Cypress.Commands.add("login", () => {
|
||||
cy.getCookie("budibase:auth").then(cookie => {
|
||||
// Already logged in
|
||||
if (cookie) return
|
||||
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
cy.contains("Create Test User").click()
|
||||
cy.get("input")
|
||||
.first()
|
||||
.type("test@test.com")
|
||||
|
||||
cy.get('input[type="password"]').type("test")
|
||||
|
||||
cy.contains("Login").click()
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add("createApp", name => {
|
||||
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
|
||||
// wait for init API calls on visit
|
||||
|
@ -44,12 +61,6 @@ Cypress.Commands.add("createApp", name => {
|
|||
.type(name)
|
||||
.should("have.value", name)
|
||||
cy.contains("Next").click()
|
||||
cy.get("input[name=email]")
|
||||
.click()
|
||||
.type("test@test.com")
|
||||
cy.get("input[name=password]")
|
||||
.click()
|
||||
.type("test")
|
||||
cy.contains("Submit").click()
|
||||
cy.get("[data-cy=new-table]", {
|
||||
timeout: 20000,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Cypress.Cookies.defaults({
|
||||
preserve: "budibase:builder:local",
|
||||
preserve: "budibase:auth",
|
||||
})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { store } from "./index"
|
||||
import { get as svelteGet } from "svelte/store"
|
||||
import { removeCookie, Cookies } from "./cookies"
|
||||
|
||||
const apiCall = method => async (
|
||||
url,
|
||||
|
@ -8,11 +9,15 @@ const apiCall = method => async (
|
|||
) => {
|
||||
headers["x-budibase-app-id"] = svelteGet(store).appId
|
||||
const json = headers["Content-Type"] === "application/json"
|
||||
return await fetch(url, {
|
||||
const resp = await fetch(url, {
|
||||
method: method,
|
||||
body: json ? JSON.stringify(body) : body,
|
||||
headers,
|
||||
})
|
||||
if (resp.status === 403) {
|
||||
removeCookie(Cookies.Auth)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
export const post = apiCall("POST")
|
||||
|
@ -20,9 +25,6 @@ export const get = apiCall("GET")
|
|||
export const patch = apiCall("PATCH")
|
||||
export const del = apiCall("DELETE")
|
||||
export const put = apiCall("PUT")
|
||||
export const getBuilderCookie = async () => {
|
||||
await post("/api/builder/login", {})
|
||||
}
|
||||
|
||||
export default {
|
||||
post: apiCall("POST"),
|
||||
|
@ -30,5 +32,4 @@ export default {
|
|||
patch: apiCall("PATCH"),
|
||||
delete: apiCall("DELETE"),
|
||||
put: apiCall("PUT"),
|
||||
getBuilderCookie,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
export const Cookies = {
|
||||
Auth: "budibase:auth",
|
||||
CurrentApp: "budibase:currentapp",
|
||||
}
|
||||
|
||||
export function getCookie(cookieName) {
|
||||
return document.cookie.split(";").some(cookie => {
|
||||
return cookie.trim().startsWith(`${cookieName}=`)
|
||||
})
|
||||
}
|
||||
|
||||
export function removeCookie(cookieName) {
|
||||
if (getCookie(cookieName)) {
|
||||
document.cookie = `${cookieName}=; Max-Age=-99999999;`
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import { derived, writable } from "svelte/store"
|
|||
import analytics from "analytics"
|
||||
import { FrontendTypes, LAYOUT_NAMES } from "../constants"
|
||||
import { findComponent } from "./storeUtils"
|
||||
import { getBuilderCookie } from "./api"
|
||||
|
||||
export const store = getFrontendStore()
|
||||
export const automationStore = getAutomationStore()
|
||||
|
@ -58,8 +57,6 @@ export const selectedAccessRole = writable("BASIC")
|
|||
|
||||
export const initialise = async () => {
|
||||
try {
|
||||
// TODO this needs to be replaced by a real login
|
||||
await getBuilderCookie()
|
||||
await analytics.activate()
|
||||
analytics.captureEvent("Builder Started")
|
||||
} catch (err) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import api from "builderStore/api"
|
||||
|
||||
export async function createUser(user) {
|
||||
const CREATE_USER_URL = `/api/users`
|
||||
const CREATE_USER_URL = `/api/users/metadata`
|
||||
const response = await api.post(CREATE_USER_URL, user)
|
||||
return await response.json()
|
||||
}
|
||||
|
@ -15,8 +15,7 @@ export async function saveRow(row, tableId) {
|
|||
|
||||
export async function deleteRow(row) {
|
||||
const DELETE_ROWS_URL = `/api/${row.tableId}/rows/${row._id}/${row._rev}`
|
||||
const response = await api.delete(DELETE_ROWS_URL)
|
||||
return response
|
||||
return api.delete(DELETE_ROWS_URL)
|
||||
}
|
||||
|
||||
export async function fetchDataForView(view) {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<script>
|
||||
import { Button, Label, Input, Spacer } from "@budibase/bbui"
|
||||
import { notifier } from "builderStore/store/notifications"
|
||||
import { auth } from "stores/backend"
|
||||
|
||||
let username = ""
|
||||
let password = ""
|
||||
|
||||
async function login() {
|
||||
try {
|
||||
await auth.login({
|
||||
username,
|
||||
password,
|
||||
})
|
||||
notifier.success("Logged in successfully.")
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
notifier.danger("Invalid credentials")
|
||||
}
|
||||
}
|
||||
|
||||
async function createTestUser() {
|
||||
try {
|
||||
await auth.createUser({
|
||||
email: "test@test.com",
|
||||
password: "test",
|
||||
roles: {},
|
||||
builder: {
|
||||
global: true,
|
||||
},
|
||||
})
|
||||
notifier.success("Test user created")
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
notifier.danger("Could not create test user")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault data-cy="login-form">
|
||||
<Spacer large />
|
||||
<Label small>Email</Label>
|
||||
<Input outline bind:value={username} />
|
||||
<Spacer large />
|
||||
<Label small>Password</Label>
|
||||
<Input outline type="password" on:change bind:value={password} />
|
||||
<Spacer large />
|
||||
<Button primary on:click={login}>Login</Button>
|
||||
<Button secondary on:click={createTestUser}>Create Test User</Button>
|
||||
</form>
|
||||
|
||||
<style>
|
||||
form {
|
||||
width: 60%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1 @@
|
|||
export { LoginForm } from "./LoginForm.svelte"
|
|
@ -25,10 +25,6 @@
|
|||
applicationName: string().required("Your application must have a name."),
|
||||
}
|
||||
const userValidation = {
|
||||
email: string()
|
||||
.email()
|
||||
.required("Your application needs a first user."),
|
||||
password: string().required("Please enter a password for your first user."),
|
||||
roleId: string().required("You need to select a role for your user."),
|
||||
}
|
||||
|
||||
|
@ -153,11 +149,9 @@
|
|||
|
||||
// Create user
|
||||
const user = {
|
||||
email: $createAppStore.values.email,
|
||||
password: $createAppStore.values.password,
|
||||
roleId: $createAppStore.values.roleId,
|
||||
}
|
||||
const userResp = await api.post(`/api/users`, user)
|
||||
const userResp = await api.post(`/api/users/metadata`, user)
|
||||
const json = await userResp.json()
|
||||
$goto(`./${appJson._id}`)
|
||||
} catch (error) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<script>
|
||||
import { TextButton as Button, Modal } from "@budibase/bbui"
|
||||
import { auth } from "stores/backend"
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Button text on:click={auth.logout}>
|
||||
<i class="ri-logout-box-line" />
|
||||
<p>Logout</p>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div i {
|
||||
font-size: 26px;
|
||||
color: var(--grey-7);
|
||||
margin-left: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div p {
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--font-size-s);
|
||||
color: var(--ink);
|
||||
font-weight: 400;
|
||||
margin: 0 0 0 12px;
|
||||
}
|
||||
</style>
|
|
@ -1,26 +1,9 @@
|
|||
<script>
|
||||
import { Input, Select } from "@budibase/bbui"
|
||||
export let validationErrors
|
||||
|
||||
let blurred = { email: false, password: false }
|
||||
</script>
|
||||
|
||||
<h2>Create your first User</h2>
|
||||
<h2>What's your role for this app?</h2>
|
||||
<div class="container">
|
||||
<Input
|
||||
on:input={() => (blurred.email = true)}
|
||||
label="Email"
|
||||
name="email"
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
error={blurred.email && validationErrors.email} />
|
||||
<Input
|
||||
on:input={() => (blurred.password = true)}
|
||||
label="Password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
error={blurred.password && validationErrors.password} />
|
||||
<Select label="Role" secondary name="roleId">
|
||||
<option value="ADMIN">Admin</option>
|
||||
<option value="POWER_USER">Power User</option>
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
{#if $datasources.list.length === 0}
|
||||
<i>Connect your first datasource to start building.</i>
|
||||
{:else}
|
||||
<i>Select a datasource to edit</i>
|
||||
{/if}
|
||||
{:else}<i>Select a datasource to edit</i>{/if}
|
||||
|
||||
<style>
|
||||
i {
|
||||
|
|
|
@ -7,44 +7,56 @@
|
|||
CommunityIcon,
|
||||
BugIcon,
|
||||
} from "components/common/Icons"
|
||||
import LoginForm from "components/login/LoginForm.svelte"
|
||||
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte"
|
||||
import LogoutButton from "components/start/LogoutButton.svelte"
|
||||
import Logo from "/assets/budibase-logo.svg"
|
||||
import { auth } from "stores/backend"
|
||||
|
||||
let modal
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="ui-nav">
|
||||
<div class="home-logo"><img src={Logo} alt="Budibase icon" /></div>
|
||||
<div class="nav-section">
|
||||
<div class="nav-top">
|
||||
<Link icon={AppsIcon} title="Apps" href="/" active />
|
||||
<Link
|
||||
icon={HostingIcon}
|
||||
title="Hosting"
|
||||
href="https://portal.budi.live/" />
|
||||
<Link
|
||||
icon={DocumentationIcon}
|
||||
title="Documentation"
|
||||
href="https://docs.budibase.com/" />
|
||||
<Link
|
||||
icon={CommunityIcon}
|
||||
title="Community"
|
||||
href="https://github.com/Budibase/budibase/discussions" />
|
||||
<Link
|
||||
icon={BugIcon}
|
||||
title="Raise an issue"
|
||||
href="https://github.com/Budibase/budibase/issues/new/choose" />
|
||||
{#if $auth}
|
||||
{#if $auth.user}
|
||||
<div class="root">
|
||||
<div class="ui-nav">
|
||||
<div class="home-logo"><img src={Logo} alt="Budibase icon" /></div>
|
||||
<div class="nav-section">
|
||||
<div class="nav-top">
|
||||
<Link icon={AppsIcon} title="Apps" href="/" active />
|
||||
<Link
|
||||
icon={HostingIcon}
|
||||
title="Hosting"
|
||||
href="https://portal.budi.live/" />
|
||||
<Link
|
||||
icon={DocumentationIcon}
|
||||
title="Documentation"
|
||||
href="https://docs.budibase.com/" />
|
||||
<Link
|
||||
icon={CommunityIcon}
|
||||
title="Community"
|
||||
href="https://github.com/Budibase/budibase/discussions" />
|
||||
<Link
|
||||
icon={BugIcon}
|
||||
title="Raise an issue"
|
||||
href="https://github.com/Budibase/budibase/issues/new/choose" />
|
||||
</div>
|
||||
<div class="nav-bottom">
|
||||
<BuilderSettingsButton />
|
||||
<LogoutButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-bottom">
|
||||
<BuilderSettingsButton />
|
||||
<div class="main">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<section class="login">
|
||||
<LoginForm />
|
||||
</section>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.root {
|
||||
|
@ -55,6 +67,14 @@
|
|||
background: var(--grey-1);
|
||||
}
|
||||
|
||||
.login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
grid-column: 2;
|
||||
overflow: auto;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import { writable } from "svelte/store"
|
||||
import api from "../../builderStore/api"
|
||||
|
||||
async function checkAuth() {
|
||||
const response = await api.get("/api/self")
|
||||
const user = await response.json()
|
||||
if (response.status === 200) return user
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function createAuthStore() {
|
||||
const { subscribe, set } = writable(null)
|
||||
|
||||
checkAuth()
|
||||
.then(user => set({ user }))
|
||||
.catch(() => set({ user: null }))
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
login: async creds => {
|
||||
const response = await api.post(`/api/admin/auth`, creds)
|
||||
const json = await response.json()
|
||||
if (response.status === 200) {
|
||||
set({ user: json.user })
|
||||
} else {
|
||||
throw "Invalid credentials"
|
||||
}
|
||||
return json
|
||||
},
|
||||
logout: async () => {
|
||||
const response = await api.post(`/api/admin/auth/logout`)
|
||||
await response.json()
|
||||
set({ user: null })
|
||||
},
|
||||
createUser: async user => {
|
||||
const response = await api.post(`/api/admin/users`, user)
|
||||
await response.json()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const auth = createAuthStore()
|
|
@ -7,3 +7,4 @@ export { roles } from "./roles"
|
|||
export { datasources } from "./datasources"
|
||||
export { integrations } from "./integrations"
|
||||
export { queries } from "./queries"
|
||||
export { auth } from "./auth"
|
||||
|
|
|
@ -13,8 +13,8 @@ export const logIn = async ({ email, password }) => {
|
|||
return API.error("Please enter your password")
|
||||
}
|
||||
return await API.post({
|
||||
url: "/api/authenticate",
|
||||
body: { email, password },
|
||||
url: "/api/admin/auth",
|
||||
body: { username: email, password },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ const createAuthStore = () => {
|
|||
|
||||
// Logs a user in
|
||||
const logIn = async ({ email, password }) => {
|
||||
const user = await API.logIn({ email, password })
|
||||
if (!user.error) {
|
||||
const auth = await API.logIn({ email, password })
|
||||
if (auth.success) {
|
||||
await fetchUser()
|
||||
await initialise()
|
||||
goToDefaultRoute()
|
||||
|
@ -30,12 +30,7 @@ const createAuthStore = () => {
|
|||
// Logs a user out
|
||||
const logOut = async () => {
|
||||
store.set(null)
|
||||
const appId = get(builderStore).appId
|
||||
if (appId) {
|
||||
for (let environment of ["local", "cloud"]) {
|
||||
window.document.cookie = `budibase:${appId}:${environment}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
|
||||
}
|
||||
}
|
||||
window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
|
||||
await initialise()
|
||||
goToDefaultRoute()
|
||||
}
|
||||
|
|
|
@ -10,8 +10,15 @@ module.exports = async (url, opts) => {
|
|||
}
|
||||
}
|
||||
|
||||
if (url.includes("/api/admin")) {
|
||||
return json({
|
||||
email: "test@test.com",
|
||||
_id: "us_test@test.com",
|
||||
status: "active",
|
||||
})
|
||||
}
|
||||
// mocked data based on url
|
||||
if (url.includes("api/apps")) {
|
||||
else if (url.includes("api/apps")) {
|
||||
return json({
|
||||
app1: {
|
||||
url: "/app1",
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
"electron": "electron src/electron.js",
|
||||
"build:electron": "electron-builder --dir",
|
||||
"publish:electron": "electron-builder -mwl --publish always",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"lint": "eslint --fix src/",
|
||||
"initialise": "node scripts/initialise.js"
|
||||
},
|
||||
|
@ -80,6 +79,7 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/auth": "^0.0.1",
|
||||
"@budibase/client": "^0.8.14",
|
||||
"@budibase/string-templates": "^0.8.14",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
|
|
|
@ -33,9 +33,6 @@ async function init() {
|
|||
fs.writeFileSync(envoyOutputPath, processStringSync(contents, config))
|
||||
|
||||
const envFilePath = path.join(process.cwd(), ".env")
|
||||
if (fs.existsSync(envFilePath)) {
|
||||
return
|
||||
}
|
||||
const envFileJson = {
|
||||
PORT: 4001,
|
||||
MINIO_URL: "http://localhost:10000/",
|
||||
|
@ -70,7 +67,11 @@ async function nuke() {
|
|||
console.log(
|
||||
"Clearing down your budibase dev environment, including all containers and volumes... 💥"
|
||||
)
|
||||
await compose.down(CONFIG)
|
||||
await compose.down({
|
||||
...CONFIG,
|
||||
// stop containers, delete volumes
|
||||
commandOptions: ["-v", "--remove-orphans"],
|
||||
})
|
||||
}
|
||||
|
||||
const managementCommand = process.argv.slice(2)[0]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const CouchDB = require("../../db")
|
||||
const env = require("../../environment")
|
||||
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
|
||||
const packageJson = require("../../../package.json")
|
||||
const {
|
||||
createLinkView,
|
||||
|
@ -74,7 +73,7 @@ async function getAppUrlIfNotInUse(ctx) {
|
|||
if (!env.SELF_HOSTED) {
|
||||
return url
|
||||
}
|
||||
const deployedApps = await getDeployedApps()
|
||||
const deployedApps = await getDeployedApps(ctx)
|
||||
if (
|
||||
deployedApps[url] != null &&
|
||||
deployedApps[url].appId !== ctx.params.appId
|
||||
|
@ -145,7 +144,6 @@ exports.fetchAppPackage = async function(ctx) {
|
|||
layouts,
|
||||
clientLibPath: clientLibraryPath(ctx.params.appId),
|
||||
}
|
||||
await setBuilderToken(ctx, ctx.params.appId, application.version)
|
||||
}
|
||||
|
||||
exports.create = async function(ctx) {
|
||||
|
@ -161,7 +159,6 @@ exports.create = async function(ctx) {
|
|||
|
||||
const url = await getAppUrlIfNotInUse(ctx)
|
||||
const appId = instance._id
|
||||
const version = packageJson.version
|
||||
const newApplication = {
|
||||
_id: appId,
|
||||
type: "app",
|
||||
|
@ -184,7 +181,6 @@ exports.create = async function(ctx) {
|
|||
await createApp(appId)
|
||||
}
|
||||
|
||||
await setBuilderToken(ctx, appId, version)
|
||||
ctx.status = 200
|
||||
ctx.body = newApplication
|
||||
ctx.message = `Application ${ctx.request.body.name} created successfully`
|
||||
|
|
|
@ -3,12 +3,12 @@ const CouchDB = require("../../db")
|
|||
const bcrypt = require("../../utilities/bcrypt")
|
||||
const env = require("../../environment")
|
||||
const { getAPIKey } = require("../../utilities/usageQuota")
|
||||
const { generateUserID } = require("../../db/utils")
|
||||
const { generateUserMetadataID } = require("../../db/utils")
|
||||
const { setCookie } = require("../../utilities")
|
||||
const { outputProcessing } = require("../../utilities/rowProcessor")
|
||||
const { ViewNames } = require("../../db/utils")
|
||||
const { UserStatus } = require("../../constants")
|
||||
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
|
||||
const { InternalTables } = require("../../db/utils")
|
||||
const { UserStatus } = require("@budibase/auth")
|
||||
const { getFullUser } = require("../../utilities/users")
|
||||
|
||||
const INVALID_ERR = "Invalid Credentials"
|
||||
|
||||
|
@ -27,7 +27,7 @@ exports.authenticate = async ctx => {
|
|||
|
||||
let dbUser
|
||||
try {
|
||||
dbUser = await db.get(generateUserID(email))
|
||||
dbUser = await db.get(generateUserMetadataID(email))
|
||||
} catch (_) {
|
||||
// do not want to throw a 404 - as this could be
|
||||
// used to determine valid emails
|
||||
|
@ -49,7 +49,7 @@ exports.authenticate = async ctx => {
|
|||
// if in prod add the user api key, unless self hosted
|
||||
/* istanbul ignore next */
|
||||
if (env.isProd() && !env.SELF_HOSTED) {
|
||||
const { apiKey } = await getAPIKey(ctx.user.appId)
|
||||
const { apiKey } = await getAPIKey(ctx.appId)
|
||||
payload.apiKey = apiKey
|
||||
}
|
||||
|
||||
|
@ -70,24 +70,36 @@ exports.authenticate = async ctx => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.builderLogin = async ctx => {
|
||||
await setBuilderToken(ctx)
|
||||
ctx.status = 200
|
||||
}
|
||||
|
||||
exports.fetchSelf = async ctx => {
|
||||
const { userId, appId } = ctx.user
|
||||
if (!ctx.user) {
|
||||
ctx.throw(403, "No user logged in")
|
||||
}
|
||||
const appId = ctx.appId
|
||||
const { userId } = ctx.user
|
||||
/* istanbul ignore next */
|
||||
if (!userId || !appId) {
|
||||
if (!userId) {
|
||||
ctx.body = {}
|
||||
return
|
||||
}
|
||||
const db = new CouchDB(appId)
|
||||
const user = await db.get(userId)
|
||||
const userTable = await db.get(ViewNames.USERS)
|
||||
if (user) {
|
||||
delete user.password
|
||||
|
||||
const user = await getFullUser({ ctx, userId: userId })
|
||||
|
||||
if (appId) {
|
||||
const db = new CouchDB(appId)
|
||||
// remove the full roles structure
|
||||
delete user.roles
|
||||
try {
|
||||
const userTable = await db.get(InternalTables.USER_METADATA)
|
||||
const metadata = await db.get(userId)
|
||||
// specifically needs to make sure is enriched
|
||||
ctx.body = await outputProcessing(appId, userTable, {
|
||||
...user,
|
||||
...metadata,
|
||||
})
|
||||
} catch (err) {
|
||||
ctx.body = user
|
||||
}
|
||||
} else {
|
||||
ctx.body = user
|
||||
}
|
||||
// specifically needs to make sure is enriched
|
||||
ctx.body = await outputProcessing(appId, userTable, user)
|
||||
}
|
||||
|
|
|
@ -34,13 +34,13 @@ function cleanAutomationInputs(automation) {
|
|||
|
||||
/**
|
||||
* This function handles checking if any webhooks need to be created or deleted for automations.
|
||||
* @param {object} user The user object, including all auth info
|
||||
* @param {string} appId The ID of the app in which we are checking for webhooks
|
||||
* @param {object|undefined} oldAuto The old automation object if updating/deleting
|
||||
* @param {object|undefined} newAuto The new automation object if creating/updating
|
||||
* @returns {Promise<object|undefined>} After this is complete the new automation object may have been updated and should be
|
||||
* written to DB (this does not write to DB as it would be wasteful to repeat).
|
||||
*/
|
||||
async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
||||
async function checkForWebhooks({ appId, oldAuto, newAuto }) {
|
||||
const oldTrigger = oldAuto ? oldAuto.definition.trigger : null
|
||||
const newTrigger = newAuto ? newAuto.definition.trigger : null
|
||||
function isWebhookTrigger(auto) {
|
||||
|
@ -56,11 +56,11 @@ async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
|||
!isWebhookTrigger(newAuto) &&
|
||||
oldTrigger.webhookId
|
||||
) {
|
||||
let db = new CouchDB(user.appId)
|
||||
let db = new CouchDB(appId)
|
||||
// need to get the webhook to get the rev
|
||||
const webhook = await db.get(oldTrigger.webhookId)
|
||||
const ctx = {
|
||||
user,
|
||||
appId,
|
||||
params: { id: webhook._id, rev: webhook._rev },
|
||||
}
|
||||
// might be updating - reset the inputs to remove the URLs
|
||||
|
@ -73,7 +73,7 @@ async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
|||
// need to create webhook
|
||||
else if (!isWebhookTrigger(oldAuto) && isWebhookTrigger(newAuto)) {
|
||||
const ctx = {
|
||||
user,
|
||||
appId,
|
||||
request: {
|
||||
body: new webhooks.Webhook(
|
||||
"Automation webhook",
|
||||
|
@ -86,17 +86,17 @@ async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
|||
const id = ctx.body.webhook._id
|
||||
newTrigger.webhookId = id
|
||||
newTrigger.inputs = {
|
||||
schemaUrl: `api/webhooks/schema/${user.appId}/${id}`,
|
||||
triggerUrl: `api/webhooks/trigger/${user.appId}/${id}`,
|
||||
schemaUrl: `api/webhooks/schema/${appId}/${id}`,
|
||||
triggerUrl: `api/webhooks/trigger/${appId}/${id}`,
|
||||
}
|
||||
}
|
||||
return newAuto
|
||||
}
|
||||
|
||||
exports.create = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
let automation = ctx.request.body
|
||||
automation.appId = ctx.user.appId
|
||||
automation.appId = ctx.appId
|
||||
|
||||
// call through to update if already exists
|
||||
if (automation._id && automation._rev) {
|
||||
|
@ -107,7 +107,10 @@ exports.create = async function(ctx) {
|
|||
|
||||
automation.type = "automation"
|
||||
automation = cleanAutomationInputs(automation)
|
||||
automation = await checkForWebhooks({ user: ctx.user, newAuto: automation })
|
||||
automation = await checkForWebhooks({
|
||||
appId: ctx.appId,
|
||||
newAuto: automation,
|
||||
})
|
||||
const response = await db.put(automation)
|
||||
automation._rev = response.rev
|
||||
|
||||
|
@ -122,13 +125,13 @@ exports.create = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.update = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
let automation = ctx.request.body
|
||||
automation.appId = ctx.user.appId
|
||||
automation.appId = ctx.appId
|
||||
const oldAutomation = await db.get(automation._id)
|
||||
automation = cleanAutomationInputs(automation)
|
||||
automation = await checkForWebhooks({
|
||||
user: ctx.user,
|
||||
appId: ctx.appId,
|
||||
oldAuto: oldAutomation,
|
||||
newAuto: automation,
|
||||
})
|
||||
|
@ -147,7 +150,7 @@ exports.update = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.fetch = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const response = await db.allDocs(
|
||||
getAutomationParams(null, {
|
||||
include_docs: true,
|
||||
|
@ -157,14 +160,17 @@ exports.fetch = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.find = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
ctx.body = await db.get(ctx.params.id)
|
||||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const oldAutomation = await db.get(ctx.params.id)
|
||||
await checkForWebhooks({ user: ctx.user, oldAuto: oldAutomation })
|
||||
await checkForWebhooks({
|
||||
appId: ctx.appId,
|
||||
oldAuto: oldAutomation,
|
||||
})
|
||||
ctx.body = await db.remove(ctx.params.id, ctx.params.rev)
|
||||
}
|
||||
|
||||
|
@ -195,11 +201,11 @@ module.exports.getDefinitionList = async function(ctx) {
|
|||
*********************/
|
||||
|
||||
exports.trigger = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
let automation = await db.get(ctx.params.id)
|
||||
await triggers.externalTrigger(automation, {
|
||||
...ctx.request.body,
|
||||
appId: ctx.user.appId,
|
||||
appId: ctx.appId,
|
||||
})
|
||||
ctx.status = 200
|
||||
ctx.body = {
|
||||
|
|
|
@ -6,7 +6,7 @@ const {
|
|||
} = require("../../db/utils")
|
||||
|
||||
exports.fetch = async function(ctx) {
|
||||
const database = new CouchDB(ctx.user.appId)
|
||||
const database = new CouchDB(ctx.appId)
|
||||
ctx.body = (
|
||||
await database.allDocs(
|
||||
getDatasourceParams(null, {
|
||||
|
@ -17,7 +17,7 @@ exports.fetch = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
|
||||
const datasource = {
|
||||
_id: generateDatasourceID(),
|
||||
|
@ -34,7 +34,7 @@ exports.save = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
|
||||
// Delete all queries for the datasource
|
||||
const rows = await db.allDocs(getQueryParams(ctx.params.datasourceId, null))
|
||||
|
@ -48,6 +48,6 @@ exports.destroy = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.find = async function(ctx) {
|
||||
const database = new CouchDB(ctx.user.appId)
|
||||
const database = new CouchDB(ctx.appId)
|
||||
ctx.body = await database.get(ctx.params.datasourceId)
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ async function deployApp(deployment) {
|
|||
|
||||
exports.fetchDeployments = async function(ctx) {
|
||||
try {
|
||||
const db = new PouchDB(ctx.user.appId)
|
||||
const db = new PouchDB(ctx.appId)
|
||||
const deploymentDoc = await db.get("_local/deployments")
|
||||
const { updated, deployments } = await checkAllDeployments(
|
||||
deploymentDoc,
|
||||
|
@ -110,7 +110,7 @@ exports.fetchDeployments = async function(ctx) {
|
|||
|
||||
exports.deploymentProgress = async function(ctx) {
|
||||
try {
|
||||
const db = new PouchDB(ctx.user.appId)
|
||||
const db = new PouchDB(ctx.appId)
|
||||
const deploymentDoc = await db.get("_local/deployments")
|
||||
ctx.body = deploymentDoc[ctx.params.deploymentId]
|
||||
} catch (err) {
|
||||
|
@ -128,7 +128,7 @@ exports.deployApp = async function(ctx) {
|
|||
hostingInfo.type === HostingTypes.CLOUD
|
||||
? require("./awsDeploy")
|
||||
: require("./selfDeploy")
|
||||
let deployment = new Deployment(ctx.user.appId)
|
||||
let deployment = new Deployment(ctx.appId)
|
||||
deployment.setStatus(DeploymentStatus.PENDING)
|
||||
deployment = await storeLocalDeploymentHistory(deployment)
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
const fetch = require("node-fetch")
|
||||
const env = require("../../environment")
|
||||
const { checkSlashesInUrl } = require("../../utilities")
|
||||
const { request } = require("../../utilities/workerRequests")
|
||||
|
||||
async function redirect(ctx, method) {
|
||||
const { path } = ctx.params
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(`${env.WORKER_URL}/api/admin/${path}`),
|
||||
request(ctx, {
|
||||
method,
|
||||
body: ctx.request.body,
|
||||
})
|
||||
)
|
||||
ctx.body = await response.json()
|
||||
const cookie = response.headers.get("set-cookie")
|
||||
if (cookie) {
|
||||
ctx.set("set-cookie", cookie)
|
||||
}
|
||||
ctx.status = response.status
|
||||
ctx.cookies
|
||||
}
|
||||
|
||||
exports.redirectGet = async ctx => {
|
||||
await redirect(ctx, "GET")
|
||||
}
|
||||
|
||||
exports.redirectPost = async ctx => {
|
||||
await redirect(ctx, "POST")
|
||||
}
|
||||
|
||||
exports.redirectDelete = async ctx => {
|
||||
await redirect(ctx, "DELETE")
|
||||
}
|
|
@ -40,5 +40,5 @@ exports.fetchUrls = async ctx => {
|
|||
}
|
||||
|
||||
exports.getDeployedApps = async ctx => {
|
||||
ctx.body = await getDeployedApps()
|
||||
ctx.body = await getDeployedApps(ctx)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ const CouchDB = require("../../db")
|
|||
const { generateLayoutID, getScreenParams } = require("../../db/utils")
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
let layout = ctx.request.body
|
||||
|
||||
if (!layout.props) {
|
||||
|
@ -22,7 +22,7 @@ exports.save = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const layoutId = ctx.params.layoutId,
|
||||
layoutRev = ctx.params.layoutRev
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ function formatResponse(resp) {
|
|||
}
|
||||
|
||||
exports.fetch = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
|
||||
const body = await db.allDocs(
|
||||
getQueryParams(null, {
|
||||
|
@ -39,7 +39,7 @@ exports.fetch = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const query = ctx.request.body
|
||||
|
||||
if (!query._id) {
|
||||
|
@ -90,7 +90,7 @@ async function enrichQueryFields(fields, parameters) {
|
|||
}
|
||||
|
||||
exports.find = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const query = enrichQueries(await db.get(ctx.params.queryId))
|
||||
// remove properties that could be dangerous in real app
|
||||
if (env.isProd()) {
|
||||
|
@ -102,7 +102,7 @@ exports.find = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.preview = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
|
||||
const datasource = await db.get(ctx.request.body.datasourceId)
|
||||
|
||||
|
@ -130,7 +130,7 @@ exports.preview = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.execute = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
|
||||
const query = await db.get(ctx.params.queryId)
|
||||
const datasource = await db.get(query.datasourceId)
|
||||
|
@ -153,7 +153,7 @@ exports.execute = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
await db.remove(ctx.params.queryId, ctx.params.revId)
|
||||
ctx.message = `Query deleted.`
|
||||
ctx.status = 200
|
||||
|
|
|
@ -10,8 +10,8 @@ const {
|
|||
const {
|
||||
generateRoleID,
|
||||
getRoleParams,
|
||||
getUserParams,
|
||||
ViewNames,
|
||||
getUserMetadataParams,
|
||||
InternalTables,
|
||||
} = require("../../db/utils")
|
||||
|
||||
const UpdateRolesOptions = {
|
||||
|
@ -28,7 +28,7 @@ const EXTERNAL_BUILTIN_ROLE_IDS = [
|
|||
]
|
||||
|
||||
async function updateRolesOnUserTable(db, roleId, updateOption) {
|
||||
const table = await db.get(ViewNames.USERS)
|
||||
const table = await db.get(InternalTables.USER_METADATA)
|
||||
const schema = table.schema
|
||||
const remove = updateOption === UpdateRolesOptions.REMOVED
|
||||
let updated = false
|
||||
|
@ -51,7 +51,7 @@ async function updateRolesOnUserTable(db, roleId, updateOption) {
|
|||
}
|
||||
|
||||
exports.fetch = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const body = await db.allDocs(
|
||||
getRoleParams(null, {
|
||||
include_docs: true,
|
||||
|
@ -79,11 +79,11 @@ exports.fetch = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.find = async function(ctx) {
|
||||
ctx.body = await getRole(ctx.user.appId, ctx.params.roleId)
|
||||
ctx.body = await getRole(ctx.appId, ctx.params.roleId)
|
||||
}
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
let { _id, name, inherits, permissionId } = ctx.request.body
|
||||
if (!_id) {
|
||||
_id = generateRoleID()
|
||||
|
@ -104,7 +104,7 @@ exports.save = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const roleId = ctx.params.roleId
|
||||
if (isBuiltin(roleId)) {
|
||||
ctx.throw(400, "Cannot delete builtin role.")
|
||||
|
@ -112,7 +112,7 @@ exports.destroy = async function(ctx) {
|
|||
// first check no users actively attached to role
|
||||
const users = (
|
||||
await db.allDocs(
|
||||
getUserParams(null, {
|
||||
getUserMetadataParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
|
|
|
@ -6,10 +6,10 @@ const {
|
|||
generateRowID,
|
||||
DocumentTypes,
|
||||
SEPARATOR,
|
||||
ViewNames,
|
||||
generateUserID,
|
||||
InternalTables,
|
||||
generateUserMetadataID,
|
||||
} = require("../../db/utils")
|
||||
const usersController = require("./user")
|
||||
const userController = require("./user")
|
||||
const {
|
||||
inputProcessing,
|
||||
outputProcessing,
|
||||
|
@ -37,18 +37,14 @@ validateJs.extend(validateJs.validators.datetime, {
|
|||
},
|
||||
})
|
||||
|
||||
async function findRow(db, appId, tableId, rowId) {
|
||||
async function findRow(ctx, db, tableId, rowId) {
|
||||
let row
|
||||
if (tableId === ViewNames.USERS) {
|
||||
let ctx = {
|
||||
params: {
|
||||
userId: rowId,
|
||||
},
|
||||
user: {
|
||||
appId,
|
||||
},
|
||||
// TODO remove special user case in future
|
||||
if (tableId === InternalTables.USER_METADATA) {
|
||||
ctx.params = {
|
||||
userId: rowId,
|
||||
}
|
||||
await usersController.find(ctx)
|
||||
await userController.findMetadata(ctx)
|
||||
row = ctx.body
|
||||
} else {
|
||||
row = await db.get(rowId)
|
||||
|
@ -60,7 +56,7 @@ async function findRow(db, appId, tableId, rowId) {
|
|||
}
|
||||
|
||||
exports.patch = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
let dbRow = await db.get(ctx.params.rowId)
|
||||
let dbTable = await db.get(dbRow.tableId)
|
||||
|
@ -96,14 +92,14 @@ exports.patch = async function(ctx) {
|
|||
table,
|
||||
})
|
||||
|
||||
// Creation of a new user goes to the user controller
|
||||
if (row.tableId === ViewNames.USERS) {
|
||||
// TODO remove special user case in future
|
||||
if (row.tableId === InternalTables.USER_METADATA) {
|
||||
// the row has been updated, need to put it into the ctx
|
||||
ctx.request.body = {
|
||||
...row,
|
||||
password: ctx.request.body.password,
|
||||
}
|
||||
await usersController.update(ctx)
|
||||
await userController.updateMetadata(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -121,7 +117,7 @@ exports.patch = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
let inputs = ctx.request.body
|
||||
inputs.tableId = ctx.params.tableId
|
||||
|
@ -134,16 +130,19 @@ exports.save = async function(ctx) {
|
|||
}
|
||||
|
||||
// if the row obj had an _id then it will have been retrieved
|
||||
const existingRow = ctx.preExisting
|
||||
if (existingRow) {
|
||||
ctx.params.rowId = inputs._id
|
||||
await exports.patch(ctx)
|
||||
return
|
||||
if (inputs._id && inputs._rev) {
|
||||
const existingRow = await db.get(inputs._id)
|
||||
if (existingRow) {
|
||||
ctx.params.rowId = inputs._id
|
||||
await exports.patch(ctx)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!inputs._rev && !inputs._id) {
|
||||
if (inputs.tableId === ViewNames.USERS) {
|
||||
inputs._id = generateUserID(inputs.email)
|
||||
// TODO remove special user case in future
|
||||
if (inputs.tableId === InternalTables.USER_METADATA) {
|
||||
inputs._id = generateUserMetadataID(inputs.email)
|
||||
} else {
|
||||
inputs._id = generateRowID(inputs.tableId)
|
||||
}
|
||||
|
@ -175,11 +174,11 @@ exports.save = async function(ctx) {
|
|||
table,
|
||||
})
|
||||
|
||||
// Creation of a new user goes to the user controller
|
||||
if (row.tableId === ViewNames.USERS) {
|
||||
// TODO remove special user case in future
|
||||
if (row.tableId === InternalTables.USER_METADATA) {
|
||||
// the row has been updated, need to put it into the ctx
|
||||
ctx.request.body = row
|
||||
await usersController.create(ctx)
|
||||
await userController.createMetadata(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -197,7 +196,7 @@ exports.save = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.fetchView = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const viewName = ctx.params.viewName
|
||||
|
||||
// if this is a table view being looked for just transfer to that
|
||||
|
@ -256,7 +255,7 @@ exports.fetchView = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.search = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const {
|
||||
query,
|
||||
|
@ -287,14 +286,6 @@ exports.search = async function(ctx) {
|
|||
}
|
||||
|
||||
const response = await search(searchString)
|
||||
|
||||
// delete passwords from users
|
||||
if (tableId === ViewNames.USERS) {
|
||||
for (let row of response.rows) {
|
||||
delete row.password
|
||||
}
|
||||
}
|
||||
|
||||
const table = await db.get(tableId)
|
||||
ctx.body = {
|
||||
rows: await outputProcessing(appId, table, response.rows),
|
||||
|
@ -303,14 +294,14 @@ exports.search = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.fetchTableRows = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
|
||||
// special case for users, fetch through the user controller
|
||||
// TODO remove special user case in future
|
||||
let rows,
|
||||
table = await db.get(ctx.params.tableId)
|
||||
if (ctx.params.tableId === ViewNames.USERS) {
|
||||
await usersController.fetch(ctx)
|
||||
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
||||
await userController.fetchMetadata(ctx)
|
||||
rows = ctx.body
|
||||
} else {
|
||||
const response = await db.allDocs(
|
||||
|
@ -324,11 +315,11 @@ exports.fetchTableRows = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.find = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
try {
|
||||
const table = await db.get(ctx.params.tableId)
|
||||
const row = await findRow(db, appId, ctx.params.tableId, ctx.params.rowId)
|
||||
const row = await findRow(ctx, db, ctx.params.tableId, ctx.params.rowId)
|
||||
ctx.body = await outputProcessing(appId, table, row)
|
||||
} catch (err) {
|
||||
ctx.throw(400, err)
|
||||
|
@ -336,7 +327,7 @@ exports.find = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const row = await db.get(ctx.params.rowId)
|
||||
if (row.tableId !== ctx.params.tableId) {
|
||||
|
@ -348,17 +339,25 @@ exports.destroy = async function(ctx) {
|
|||
row,
|
||||
tableId: row.tableId,
|
||||
})
|
||||
ctx.body = await db.remove(ctx.params.rowId, ctx.params.revId)
|
||||
ctx.status = 200
|
||||
// TODO remove special user case in future
|
||||
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
||||
ctx.params = {
|
||||
userId: ctx.params.rowId,
|
||||
}
|
||||
await userController.destroyMetadata(ctx)
|
||||
} else {
|
||||
ctx.body = await db.remove(ctx.params.rowId, ctx.params.revId)
|
||||
}
|
||||
|
||||
// for automations include the row that was deleted
|
||||
ctx.row = row
|
||||
ctx.status = 200
|
||||
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
|
||||
}
|
||||
|
||||
exports.validate = async function(ctx) {
|
||||
const errors = await validate({
|
||||
appId: ctx.user.appId,
|
||||
appId: ctx.appId,
|
||||
tableId: ctx.params.tableId,
|
||||
row: ctx.request.body,
|
||||
})
|
||||
|
@ -388,14 +387,14 @@ async function validate({ appId, tableId, row, table }) {
|
|||
}
|
||||
|
||||
exports.fetchEnrichedRow = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const tableId = ctx.params.tableId
|
||||
const rowId = ctx.params.rowId
|
||||
// need table to work out where links go in row
|
||||
let [table, row] = await Promise.all([
|
||||
db.get(tableId),
|
||||
findRow(db, appId, tableId, rowId),
|
||||
findRow(ctx, db, tableId, rowId),
|
||||
])
|
||||
// get the link docs
|
||||
const linkVals = await linkRows.getLinkDocuments({
|
||||
|
@ -433,11 +432,11 @@ exports.fetchEnrichedRow = async function(ctx) {
|
|||
}
|
||||
|
||||
async function bulkDelete(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const { rows } = ctx.request.body
|
||||
const db = new CouchDB(appId)
|
||||
|
||||
const linkUpdates = rows.map(row =>
|
||||
let updates = rows.map(row =>
|
||||
linkRows.updateLinks({
|
||||
appId,
|
||||
eventType: linkRows.EventType.ROW_DELETE,
|
||||
|
@ -445,9 +444,20 @@ async function bulkDelete(ctx) {
|
|||
tableId: row.tableId,
|
||||
})
|
||||
)
|
||||
|
||||
await db.bulkDocs(rows.map(row => ({ ...row, _deleted: true })))
|
||||
await Promise.all(linkUpdates)
|
||||
// TODO remove special user case in future
|
||||
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
||||
updates = updates.concat(
|
||||
rows.map(row => {
|
||||
ctx.params = {
|
||||
userId: row._id,
|
||||
}
|
||||
return userController.destroyMetadata(ctx)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
await db.bulkDocs(rows.map(row => ({ ...row, _deleted: true })))
|
||||
}
|
||||
await Promise.all(updates)
|
||||
|
||||
rows.forEach(row => {
|
||||
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
|
||||
|
|
|
@ -3,7 +3,7 @@ const { getScreenParams, generateScreenID } = require("../../db/utils")
|
|||
const { AccessController } = require("../../utilities/security/roles")
|
||||
|
||||
exports.fetch = async ctx => {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
|
||||
const screens = (
|
||||
|
@ -21,7 +21,7 @@ exports.fetch = async ctx => {
|
|||
}
|
||||
|
||||
exports.save = async ctx => {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
let screen = ctx.request.body
|
||||
|
||||
|
@ -39,7 +39,7 @@ exports.save = async ctx => {
|
|||
}
|
||||
|
||||
exports.destroy = async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
await db.remove(ctx.params.screenId, ctx.params.screenRev)
|
||||
ctx.body = {
|
||||
message: "Screen deleted successfully",
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const { QueryBuilder, buildSearchUrl, search } = require("./utils")
|
||||
|
||||
exports.rowSearch = async ctx => {
|
||||
// this can't be done through pouch, have to reach for trusty node-fetch
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const { tableId } = ctx.params
|
||||
const { bookmark, query, raw } = ctx.request.body
|
||||
let url
|
||||
|
|
|
@ -9,7 +9,6 @@ const { processString } = require("@budibase/string-templates")
|
|||
const { budibaseTempDir } = require("../../../utilities/budibaseDir")
|
||||
const { getDeployedApps } = require("../../../utilities/builder/hosting")
|
||||
const CouchDB = require("../../../db")
|
||||
const setBuilderToken = require("../../../utilities/builder/setBuilderToken")
|
||||
const {
|
||||
loadHandlebarsFile,
|
||||
NODE_MODULES_PATH,
|
||||
|
@ -22,7 +21,7 @@ const { objectStoreUrl, clientLibraryPath } = require("../../../utilities")
|
|||
async function checkForSelfHostedURL(ctx) {
|
||||
// the "appId" component of the URL may actually be a specific self hosted URL
|
||||
let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`
|
||||
const apps = await getDeployedApps()
|
||||
const apps = await getDeployedApps(ctx)
|
||||
if (apps[possibleAppUrl] && apps[possibleAppUrl].appId) {
|
||||
return apps[possibleAppUrl].appId
|
||||
} else {
|
||||
|
@ -35,9 +34,6 @@ const COMP_LIB_BASE_APP_VERSION = "0.2.5"
|
|||
|
||||
exports.serveBuilder = async function(ctx) {
|
||||
let builderPath = resolve(TOP_LEVEL_PATH, "builder")
|
||||
if (ctx.file === "index.html") {
|
||||
await setBuilderToken(ctx)
|
||||
}
|
||||
await send(ctx, ctx.file, { root: builderPath })
|
||||
}
|
||||
|
||||
|
@ -60,7 +56,7 @@ exports.uploadFile = async function(ctx) {
|
|||
|
||||
return prepareUpload({
|
||||
file,
|
||||
s3Key: `assets/${ctx.user.appId}/attachments/${processedFileName}`,
|
||||
s3Key: `assets/${ctx.appId}/attachments/${processedFileName}`,
|
||||
bucket: "prod-budi-app-assets",
|
||||
})
|
||||
})
|
||||
|
@ -110,20 +106,17 @@ exports.serveComponentLibrary = async function(ctx) {
|
|||
)
|
||||
return send(ctx, "/awsDeploy.js", { root: componentLibraryPath })
|
||||
}
|
||||
const db = new CouchDB(appId)
|
||||
const appInfo = await db.get(appId)
|
||||
|
||||
let componentLib = "componentlibrary"
|
||||
if (ctx.user.version) {
|
||||
componentLib += `-${ctx.user.version}`
|
||||
if (appInfo && appInfo.version) {
|
||||
componentLib += `-${appInfo.version}`
|
||||
} else {
|
||||
componentLib += `-${COMP_LIB_BASE_APP_VERSION}`
|
||||
}
|
||||
const S3_URL = encodeURI(
|
||||
join(
|
||||
objectStoreUrl(appId),
|
||||
componentLib,
|
||||
ctx.query.library,
|
||||
"dist",
|
||||
"index.js"
|
||||
)
|
||||
join(objectStoreUrl(), componentLib, ctx.query.library, "dist", "index.js")
|
||||
)
|
||||
const response = await fetch(S3_URL)
|
||||
const body = await response.text()
|
||||
|
|
|
@ -10,7 +10,7 @@ const { FieldTypes } = require("../../../constants")
|
|||
const { TableSaveFunctions } = require("./utils")
|
||||
|
||||
exports.fetch = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const body = await db.allDocs(
|
||||
getTableParams(null, {
|
||||
include_docs: true,
|
||||
|
@ -20,12 +20,12 @@ exports.fetch = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.find = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
ctx.body = await db.get(ctx.params.id)
|
||||
}
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const { dataImport, ...rest } = ctx.request.body
|
||||
let tableToSave = {
|
||||
|
@ -127,7 +127,7 @@ exports.save = async function(ctx) {
|
|||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const appId = ctx.user.appId
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const tableToDelete = await db.get(ctx.params.tableId)
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
const CouchDB = require("../../../db")
|
||||
const csvParser = require("../../../utilities/csvParser")
|
||||
const { getRowParams, generateRowID, ViewNames } = require("../../../db/utils")
|
||||
const {
|
||||
getRowParams,
|
||||
generateRowID,
|
||||
InternalTables,
|
||||
} = require("../../../db/utils")
|
||||
const { isEqual } = require("lodash/fp")
|
||||
const { AutoFieldSubTypes } = require("../../../constants")
|
||||
const { inputProcessing } = require("../../../utilities/rowProcessor")
|
||||
|
@ -57,8 +61,8 @@ exports.makeSureTableUpToDate = (table, tableToSave) => {
|
|||
return tableToSave
|
||||
}
|
||||
|
||||
exports.handleDataImport = async (user, table, dataImport) => {
|
||||
const db = new CouchDB(user.appId)
|
||||
exports.handleDataImport = async (appId, user, table, dataImport) => {
|
||||
const db = new CouchDB(appId)
|
||||
if (dataImport && dataImport.csvString) {
|
||||
// Populate the table with rows imported from CSV in a bulk update
|
||||
const data = await csvParser.transform(dataImport)
|
||||
|
@ -136,7 +140,7 @@ exports.handleSearchIndexes = async (appId, table) => {
|
|||
|
||||
exports.checkStaticTables = table => {
|
||||
// check user schema has all required elements
|
||||
if (table._id === ViewNames.USERS) {
|
||||
if (table._id === InternalTables.USER_METADATA) {
|
||||
for (let [key, schema] of Object.entries(USERS_TABLE_SCHEMA.schema)) {
|
||||
// check if the schema exists on the table to be created/updated
|
||||
if (table.schema[key] == null) {
|
||||
|
@ -152,7 +156,7 @@ class TableSaveFunctions {
|
|||
this.db = db
|
||||
this.ctx = ctx
|
||||
if (this.ctx && this.ctx.user) {
|
||||
this.appId = this.ctx.user.appId
|
||||
this.appId = this.ctx.appId
|
||||
}
|
||||
this.oldTable = oldTable
|
||||
this.dataImport = dataImport
|
||||
|
@ -184,6 +188,7 @@ class TableSaveFunctions {
|
|||
async after(table) {
|
||||
table = await exports.handleSearchIndexes(this.appId, table)
|
||||
table = await exports.handleDataImport(
|
||||
this.appId,
|
||||
this.ctx.user,
|
||||
table,
|
||||
this.dataImport
|
||||
|
|
|
@ -1,115 +1,110 @@
|
|||
const CouchDB = require("../../db")
|
||||
const bcrypt = require("../../utilities/bcrypt")
|
||||
const { generateUserID, getUserParams, ViewNames } = require("../../db/utils")
|
||||
const {
|
||||
generateUserMetadataID,
|
||||
getUserMetadataParams,
|
||||
getEmailFromUserMetadataID,
|
||||
} = require("../../db/utils")
|
||||
const { InternalTables } = require("../../db/utils")
|
||||
const { getRole } = require("../../utilities/security/roles")
|
||||
const { UserStatus } = require("../../constants")
|
||||
const {
|
||||
getGlobalUsers,
|
||||
saveGlobalUser,
|
||||
deleteGlobalUser,
|
||||
} = require("../../utilities/workerRequests")
|
||||
const { getFullUser } = require("../../utilities/users")
|
||||
|
||||
exports.fetch = async function(ctx) {
|
||||
const database = new CouchDB(ctx.user.appId)
|
||||
const users = (
|
||||
exports.fetchMetadata = async function(ctx) {
|
||||
const database = new CouchDB(ctx.appId)
|
||||
const global = await getGlobalUsers(ctx, ctx.appId)
|
||||
const metadata = (
|
||||
await database.allDocs(
|
||||
getUserParams(null, {
|
||||
getUserMetadataParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
).rows.map(row => row.doc)
|
||||
// user hashed password shouldn't ever be returned
|
||||
for (let user of users) {
|
||||
delete user.password
|
||||
const users = []
|
||||
for (let user of global) {
|
||||
const info = metadata.find(meta => meta._id.includes(user.email))
|
||||
// remove these props, not for the correct DB
|
||||
delete user._id
|
||||
delete user._rev
|
||||
users.push({
|
||||
...user,
|
||||
...info,
|
||||
// make sure the ID is always a local ID, not a global one
|
||||
_id: generateUserMetadataID(user.email),
|
||||
})
|
||||
}
|
||||
ctx.body = users
|
||||
}
|
||||
|
||||
exports.create = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const { email, password, roleId } = ctx.request.body
|
||||
|
||||
if (!email || !password) {
|
||||
ctx.throw(400, "email and Password Required.")
|
||||
}
|
||||
|
||||
const role = await getRole(ctx.user.appId, roleId)
|
||||
exports.createMetadata = async function(ctx) {
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const { roleId } = ctx.request.body
|
||||
const email = ctx.request.body.email || ctx.user.email
|
||||
|
||||
// check role valid
|
||||
const role = await getRole(appId, roleId)
|
||||
if (!role) ctx.throw(400, "Invalid Role")
|
||||
|
||||
const hashedPassword = await bcrypt.hash(password)
|
||||
const metadata = await saveGlobalUser(ctx, appId, email, ctx.request.body)
|
||||
|
||||
const user = {
|
||||
...ctx.request.body,
|
||||
// these must all be after the object spread, make sure
|
||||
// any values are overwritten, generateUserID will always
|
||||
// generate the same ID for the user as it is not UUID based
|
||||
_id: generateUserID(email),
|
||||
...metadata,
|
||||
_id: generateUserMetadataID(email),
|
||||
type: "user",
|
||||
password: hashedPassword,
|
||||
tableId: ViewNames.USERS,
|
||||
}
|
||||
// add the active status to a user if its not provided
|
||||
if (user.status == null) {
|
||||
user.status = UserStatus.ACTIVE
|
||||
tableId: InternalTables.USER_METADATA,
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await db.post(user)
|
||||
ctx.status = 200
|
||||
ctx.message = "User created successfully."
|
||||
ctx.userId = response.id
|
||||
ctx.body = {
|
||||
_rev: response.rev,
|
||||
email,
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.status === 409) {
|
||||
ctx.throw(400, "User exists already")
|
||||
} else {
|
||||
ctx.throw(err.status, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.update = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const user = ctx.request.body
|
||||
let dbUser
|
||||
if (user.email && !user._id) {
|
||||
user._id = generateUserID(user.email)
|
||||
}
|
||||
// get user incase password removed
|
||||
if (user._id) {
|
||||
dbUser = await db.get(user._id)
|
||||
}
|
||||
if (user.password) {
|
||||
user.password = await bcrypt.hash(user.password)
|
||||
} else {
|
||||
delete user.password
|
||||
}
|
||||
|
||||
const response = await db.put({
|
||||
password: dbUser.password,
|
||||
...user,
|
||||
})
|
||||
user._rev = response.rev
|
||||
|
||||
const response = await db.post(user)
|
||||
// for automations to make it obvious was successful
|
||||
ctx.status = 200
|
||||
ctx.body = response
|
||||
ctx.body = {
|
||||
_id: response.id,
|
||||
_rev: response.rev,
|
||||
email,
|
||||
}
|
||||
}
|
||||
|
||||
exports.destroy = async function(ctx) {
|
||||
const database = new CouchDB(ctx.user.appId)
|
||||
await database.destroy(generateUserID(ctx.params.email))
|
||||
exports.updateMetadata = async function(ctx) {
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const user = ctx.request.body
|
||||
let email = user.email || getEmailFromUserMetadataID(user._id)
|
||||
const metadata = await saveGlobalUser(ctx, appId, email, ctx.request.body)
|
||||
if (!metadata._id) {
|
||||
metadata._id = generateUserMetadataID(email)
|
||||
}
|
||||
if (!metadata._rev) {
|
||||
metadata._rev = ctx.request.body._rev
|
||||
}
|
||||
ctx.body = await db.put({
|
||||
...metadata,
|
||||
})
|
||||
}
|
||||
|
||||
exports.destroyMetadata = async function(ctx) {
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const email =
|
||||
ctx.params.email || getEmailFromUserMetadataID(ctx.params.userId)
|
||||
await deleteGlobalUser(ctx, email)
|
||||
try {
|
||||
const dbUser = await db.get(generateUserMetadataID(email))
|
||||
await db.remove(dbUser._id, dbUser._rev)
|
||||
} catch (err) {
|
||||
// error just means the global user has no config in this app
|
||||
}
|
||||
ctx.body = {
|
||||
message: `User ${ctx.params.email} deleted.`,
|
||||
}
|
||||
ctx.status = 200
|
||||
}
|
||||
|
||||
exports.find = async function(ctx) {
|
||||
const database = new CouchDB(ctx.user.appId)
|
||||
let lookup = ctx.params.email
|
||||
? generateUserID(ctx.params.email)
|
||||
: ctx.params.userId
|
||||
const user = await database.get(lookup)
|
||||
if (user) {
|
||||
delete user.password
|
||||
}
|
||||
ctx.body = user
|
||||
exports.findMetadata = async function(ctx) {
|
||||
ctx.body = await getFullUser({
|
||||
ctx,
|
||||
email: ctx.params.email,
|
||||
userId: ctx.params.userId,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ const { ViewNames } = require("../../../db/utils")
|
|||
|
||||
const controller = {
|
||||
fetch: async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const designDoc = await db.get("_design/database")
|
||||
const response = []
|
||||
|
||||
|
@ -25,7 +25,7 @@ const controller = {
|
|||
ctx.body = response
|
||||
},
|
||||
save: async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const { originalName, ...viewToSave } = ctx.request.body
|
||||
const designDoc = await db.get("_design/database")
|
||||
const view = viewTemplate(viewToSave)
|
||||
|
@ -66,7 +66,7 @@ const controller = {
|
|||
}
|
||||
},
|
||||
destroy: async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const designDoc = await db.get("_design/database")
|
||||
const viewName = decodeURI(ctx.params.viewName)
|
||||
const view = designDoc.views[viewName]
|
||||
|
@ -81,7 +81,7 @@ const controller = {
|
|||
ctx.body = view
|
||||
},
|
||||
exportView: async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const designDoc = await db.get("_design/database")
|
||||
const viewName = decodeURI(ctx.query.view)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ exports.WebhookType = {
|
|||
}
|
||||
|
||||
exports.fetch = async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const response = await db.allDocs(
|
||||
getWebhookParams(null, {
|
||||
include_docs: true,
|
||||
|
@ -32,9 +32,9 @@ exports.fetch = async ctx => {
|
|||
}
|
||||
|
||||
exports.save = async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
const webhook = ctx.request.body
|
||||
webhook.appId = ctx.user.appId
|
||||
webhook.appId = ctx.appId
|
||||
|
||||
// check that the webhook exists
|
||||
if (webhook._id) {
|
||||
|
@ -51,7 +51,7 @@ exports.save = async ctx => {
|
|||
}
|
||||
|
||||
exports.destroy = async ctx => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
const db = new CouchDB(ctx.appId)
|
||||
ctx.body = await db.remove(ctx.params.id, ctx.params.rev)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const Router = require("@koa/router")
|
||||
const authenticated = require("../middleware/authenticated")
|
||||
const { authenticated } = require("@budibase/auth")
|
||||
const currentApp = require("../middleware/currentapp")
|
||||
const compress = require("koa-compress")
|
||||
const zlib = require("zlib")
|
||||
const { mainRoutes, authRoutes, staticRoutes } = require("./routes")
|
||||
|
@ -13,10 +14,10 @@ router
|
|||
compress({
|
||||
threshold: 2048,
|
||||
gzip: {
|
||||
flush: zlib.Z_SYNC_FLUSH,
|
||||
flush: zlib.constants.Z_SYNC_FLUSH,
|
||||
},
|
||||
deflate: {
|
||||
flush: zlib.Z_SYNC_FLUSH,
|
||||
flush: zlib.constants.Z_SYNC_FLUSH,
|
||||
},
|
||||
br: false,
|
||||
})
|
||||
|
@ -31,6 +32,7 @@ router
|
|||
.use("/health", ctx => (ctx.status = 200))
|
||||
.use("/version", ctx => (ctx.body = pkg.version))
|
||||
.use(authenticated)
|
||||
.use(currentApp)
|
||||
|
||||
// error handling middleware
|
||||
router.use(async (ctx, next) => {
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
const Router = require("@koa/router")
|
||||
const controller = require("../controllers/auth")
|
||||
const authorized = require("../../middleware/authorized")
|
||||
const { BUILDER } = require("../../utilities/security/permissions")
|
||||
|
||||
const router = Router()
|
||||
|
||||
router.post("/api/authenticate", controller.authenticate)
|
||||
// TODO: this is a hack simply to make sure builder has a cookie until auth reworked
|
||||
router.post("/api/builder/login", authorized(BUILDER), controller.builderLogin)
|
||||
// doesn't need authorization as can only fetch info about self
|
||||
router.get("/api/self", controller.fetchSelf)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
const Router = require("@koa/router")
|
||||
const controller = require("../controllers/dev")
|
||||
const env = require("../../environment")
|
||||
|
||||
const router = Router()
|
||||
|
||||
if (env.isDev() || env.isTest()) {
|
||||
router.get("/api/admin/:path", controller.redirectGet)
|
||||
router.post("/api/admin/:path", controller.redirectPost)
|
||||
router.delete("/api/admin/:path", controller.redirectDelete)
|
||||
}
|
||||
|
||||
module.exports = router
|
|
@ -22,6 +22,7 @@ const datasourceRoutes = require("./datasource")
|
|||
const queryRoutes = require("./query")
|
||||
const hostingRoutes = require("./hosting")
|
||||
const backupRoutes = require("./backup")
|
||||
const devRoutes = require("./dev")
|
||||
|
||||
exports.mainRoutes = [
|
||||
deployRoutes,
|
||||
|
@ -44,6 +45,7 @@ exports.mainRoutes = [
|
|||
queryRoutes,
|
||||
hostingRoutes,
|
||||
backupRoutes,
|
||||
devRoutes,
|
||||
// these need to be handled last as they still use /api/:tableId
|
||||
// this could be breaking as koa may recognise other routes as this
|
||||
tableRoutes,
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
const setup = require("./utilities")
|
||||
|
||||
require("../../../utilities/workerRequests")
|
||||
jest.mock("../../../utilities/workerRequests", () => ({
|
||||
getGlobalUsers: jest.fn(() => {
|
||||
return {
|
||||
email: "test@test.com",
|
||||
}
|
||||
}),
|
||||
saveGlobalUser: jest.fn(),
|
||||
}))
|
||||
|
||||
describe("/authenticate", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
|
@ -10,88 +20,8 @@ describe("/authenticate", () => {
|
|||
await config.init()
|
||||
})
|
||||
|
||||
describe("authenticate", () => {
|
||||
it("should be able to create a layout", async () => {
|
||||
await config.createUser("test@test.com", "p4ssw0rd")
|
||||
const res = await request
|
||||
.post(`/api/authenticate`)
|
||||
.send({
|
||||
email: "test@test.com",
|
||||
password: "p4ssw0rd",
|
||||
})
|
||||
.set(config.publicHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body.token).toBeDefined()
|
||||
expect(res.body.email).toEqual("test@test.com")
|
||||
expect(res.body.password).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should error if no app specified", async () => {
|
||||
await request
|
||||
.post(`/api/authenticate`)
|
||||
.expect(400)
|
||||
})
|
||||
|
||||
it("should error if no email specified", async () => {
|
||||
await request
|
||||
.post(`/api/authenticate`)
|
||||
.send({
|
||||
password: "test",
|
||||
})
|
||||
.set(config.publicHeaders())
|
||||
.expect(400)
|
||||
})
|
||||
|
||||
it("should error if no password specified", async () => {
|
||||
await request
|
||||
.post(`/api/authenticate`)
|
||||
.send({
|
||||
email: "test",
|
||||
})
|
||||
.set(config.publicHeaders())
|
||||
.expect(400)
|
||||
})
|
||||
|
||||
it("should error if invalid user specified", async () => {
|
||||
await request
|
||||
.post(`/api/authenticate`)
|
||||
.send({
|
||||
email: "test",
|
||||
password: "test",
|
||||
})
|
||||
.set(config.publicHeaders())
|
||||
.expect(401)
|
||||
})
|
||||
|
||||
it("should throw same error if wrong password specified", async () => {
|
||||
await config.createUser("test@test.com", "password")
|
||||
await request
|
||||
.post(`/api/authenticate`)
|
||||
.send({
|
||||
email: "test@test.com",
|
||||
password: "test",
|
||||
})
|
||||
.set(config.publicHeaders())
|
||||
.expect(401)
|
||||
})
|
||||
|
||||
it("should throw an error for inactive users", async () => {
|
||||
await config.createUser("test@test.com", "password")
|
||||
await config.makeUserInactive("test@test.com")
|
||||
await request
|
||||
.post(`/api/authenticate`)
|
||||
.send({
|
||||
email: "test@test.com",
|
||||
password: "password",
|
||||
})
|
||||
.set(config.publicHeaders())
|
||||
.expect(401)
|
||||
})
|
||||
})
|
||||
|
||||
describe("fetch self", () => {
|
||||
it("should be able to delete the layout", async () => {
|
||||
it("should be able to fetch self", async () => {
|
||||
await config.createUser("test@test.com", "p4ssw0rd")
|
||||
const headers = await config.login("test@test.com", "p4ssw0rd")
|
||||
const res = await request
|
||||
|
|
|
@ -41,10 +41,12 @@ describe("run misc tests", () => {
|
|||
const dataImport = {
|
||||
csvString: "a,b,c,d\n1,2,3,4"
|
||||
}
|
||||
await tableUtils.handleDataImport({
|
||||
appId: config.getAppId(),
|
||||
userId: "test",
|
||||
}, table, dataImport)
|
||||
await tableUtils.handleDataImport(
|
||||
config.getAppId(),
|
||||
{ userId: "test" },
|
||||
table,
|
||||
dataImport
|
||||
)
|
||||
const rows = await config.getRows()
|
||||
expect(rows[0].a).toEqual("1")
|
||||
expect(rows[0].b).toEqual("2")
|
||||
|
|
|
@ -2,6 +2,12 @@ const setup = require("./utilities")
|
|||
const { basicScreen } = setup.structures
|
||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||
const workerRequests = require("../../../utilities/workerRequests")
|
||||
|
||||
jest.mock("../../../utilities/workerRequests", () => ({
|
||||
getGlobalUsers: jest.fn(),
|
||||
saveGlobalUser: jest.fn(),
|
||||
}))
|
||||
|
||||
const route = "/test"
|
||||
|
||||
|
@ -25,6 +31,13 @@ describe("/routing", () => {
|
|||
|
||||
describe("fetch", () => {
|
||||
it("returns the correct routing for basic user", async () => {
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
|
||||
return {
|
||||
roles: {
|
||||
[appId]: BUILTIN_ROLE_IDS.BASIC,
|
||||
}
|
||||
}
|
||||
})
|
||||
const res = await request
|
||||
.get(`/api/routing/client`)
|
||||
.set(await config.roleHeaders("basic@test.com", BUILTIN_ROLE_IDS.BASIC))
|
||||
|
@ -42,6 +55,13 @@ describe("/routing", () => {
|
|||
})
|
||||
|
||||
it("returns the correct routing for power user", async () => {
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce((ctx, appId) => {
|
||||
return {
|
||||
roles: {
|
||||
[appId]: BUILTIN_ROLE_IDS.POWER,
|
||||
}
|
||||
}
|
||||
})
|
||||
const res = await request
|
||||
.get(`/api/routing/client`)
|
||||
.set(await config.roleHeaders("basic@test.com", BUILTIN_ROLE_IDS.POWER))
|
||||
|
|
|
@ -2,6 +2,15 @@ const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
|||
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
|
||||
const setup = require("./utilities")
|
||||
const { basicUser } = setup.structures
|
||||
const workerRequests = require("../../../utilities/workerRequests")
|
||||
|
||||
jest.mock("../../../utilities/workerRequests", () => ({
|
||||
getGlobalUsers: jest.fn(),
|
||||
saveGlobalUser: jest.fn(() => {
|
||||
return {}
|
||||
}),
|
||||
deleteGlobalUser: jest.fn(),
|
||||
}))
|
||||
|
||||
describe("/users", () => {
|
||||
let request = setup.getRequest()
|
||||
|
@ -14,11 +23,23 @@ describe("/users", () => {
|
|||
})
|
||||
|
||||
describe("fetch", () => {
|
||||
beforeEach(() => {
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
||||
{
|
||||
email: "brenda@brenda.com"
|
||||
},
|
||||
{
|
||||
email: "pam@pam.com"
|
||||
}
|
||||
]
|
||||
))
|
||||
})
|
||||
|
||||
it("returns a list of users from an instance db", async () => {
|
||||
await config.createUser("brenda@brenda.com", "brendas_password")
|
||||
await config.createUser("pam@pam.com", "pam_password")
|
||||
const res = await request
|
||||
.get(`/api/users`)
|
||||
.get(`/api/users/metadata`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
@ -34,7 +55,7 @@ describe("/users", () => {
|
|||
config,
|
||||
request,
|
||||
method: "GET",
|
||||
url: `/api/users`,
|
||||
url: `/api/users/metadata`,
|
||||
passRole: BUILTIN_ROLE_IDS.ADMIN,
|
||||
failRole: BUILTIN_ROLE_IDS.PUBLIC,
|
||||
})
|
||||
|
@ -42,9 +63,21 @@ describe("/users", () => {
|
|||
})
|
||||
|
||||
describe("create", () => {
|
||||
beforeEach(() => {
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ([
|
||||
{
|
||||
email: "bill@budibase.com"
|
||||
},
|
||||
{
|
||||
email: "brandNewUser@user.com"
|
||||
}
|
||||
]
|
||||
))
|
||||
})
|
||||
|
||||
async function create(user, status = 200) {
|
||||
return request
|
||||
.post(`/api/users`)
|
||||
.post(`/api/users/metadata`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(user)
|
||||
.expect(status)
|
||||
|
@ -56,8 +89,8 @@ describe("/users", () => {
|
|||
body.email = "bill@budibase.com"
|
||||
const res = await create(body)
|
||||
|
||||
expect(res.res.statusMessage).toEqual("User created successfully.")
|
||||
expect(res.body._id).toBeUndefined()
|
||||
expect(res.res.statusMessage).toEqual("OK")
|
||||
expect(res.body._id).toBeDefined()
|
||||
})
|
||||
|
||||
it("should apply authorization to endpoint", async () => {
|
||||
|
@ -67,18 +100,12 @@ describe("/users", () => {
|
|||
config,
|
||||
method: "POST",
|
||||
body,
|
||||
url: `/api/users`,
|
||||
url: `/api/users/metadata`,
|
||||
passRole: BUILTIN_ROLE_IDS.ADMIN,
|
||||
failRole: BUILTIN_ROLE_IDS.PUBLIC,
|
||||
})
|
||||
})
|
||||
|
||||
it("should error if no email provided", async () => {
|
||||
const user = basicUser(BUILTIN_ROLE_IDS.POWER)
|
||||
delete user.email
|
||||
await create(user, 400)
|
||||
})
|
||||
|
||||
it("should error if no role provided", async () => {
|
||||
const user = basicUser(null)
|
||||
await create(user, 400)
|
||||
|
@ -88,16 +115,22 @@ describe("/users", () => {
|
|||
await config.createUser("test@test.com")
|
||||
const user = basicUser(BUILTIN_ROLE_IDS.POWER)
|
||||
user.email = "test@test.com"
|
||||
await create(user, 400)
|
||||
await create(user, 409)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
beforeEach(() => {
|
||||
workerRequests.saveGlobalUser.mockImplementationOnce(() => ({
|
||||
_id: "us_test@test.com"
|
||||
}))
|
||||
})
|
||||
|
||||
it("should be able to update the user", async () => {
|
||||
const user = await config.createUser()
|
||||
user.roleId = BUILTIN_ROLE_IDS.BASIC
|
||||
const res = await request
|
||||
.put(`/api/users`)
|
||||
.put(`/api/users/metadata`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(user)
|
||||
.expect(200)
|
||||
|
@ -111,20 +144,29 @@ describe("/users", () => {
|
|||
const email = "test@test.com"
|
||||
await config.createUser(email)
|
||||
const res = await request
|
||||
.delete(`/api/users/${email}`)
|
||||
.delete(`/api/users/metadata/${email}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
expect(res.body.message).toBeDefined()
|
||||
expect(workerRequests.deleteGlobalUser).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("find", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks()
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ({
|
||||
email: "test@test.com",
|
||||
roleId: BUILTIN_ROLE_IDS.POWER,
|
||||
}))
|
||||
})
|
||||
|
||||
it("should be able to find the user", async () => {
|
||||
const email = "test@test.com"
|
||||
await config.createUser(email)
|
||||
const res = await request
|
||||
.get(`/api/users/${email}`)
|
||||
.get(`/api/users/metadata/${email}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
.expect("Content-Type", /json/)
|
||||
|
|
|
@ -3,7 +3,7 @@ const appController = require("../../../controllers/application")
|
|||
const CouchDB = require("../../../../db")
|
||||
|
||||
function Request(appId, params) {
|
||||
this.user = { appId }
|
||||
this.appId = appId
|
||||
this.params = params
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,11 @@ exports.checkPermissionsEndpoint = async ({
|
|||
}) => {
|
||||
const password = "PASSWORD"
|
||||
await config.createUser("passUser@budibase.com", password, passRole)
|
||||
const passHeader = await config.login("passUser@budibase.com", password)
|
||||
const passHeader = await config.login(
|
||||
"passUser@budibase.com",
|
||||
password,
|
||||
passRole
|
||||
)
|
||||
|
||||
await exports
|
||||
.createRequest(config.request, method, url, body)
|
||||
|
@ -71,7 +75,11 @@ exports.checkPermissionsEndpoint = async ({
|
|||
.expect(200)
|
||||
|
||||
await config.createUser("failUser@budibase.com", password, failRole)
|
||||
const failHeader = await config.login("failUser@budibase.com", password)
|
||||
const failHeader = await config.login(
|
||||
"failUser@budibase.com",
|
||||
password,
|
||||
failRole
|
||||
)
|
||||
|
||||
await exports
|
||||
.createRequest(config.request, method, url, body)
|
||||
|
|
|
@ -11,31 +11,31 @@ const router = Router()
|
|||
|
||||
router
|
||||
.get(
|
||||
"/api/users",
|
||||
"/api/users/metadata",
|
||||
authorized(PermissionTypes.USER, PermissionLevels.READ),
|
||||
controller.fetch
|
||||
controller.fetchMetadata
|
||||
)
|
||||
.get(
|
||||
"/api/users/:email",
|
||||
"/api/users/metadata/:email",
|
||||
authorized(PermissionTypes.USER, PermissionLevels.READ),
|
||||
controller.find
|
||||
controller.findMetadata
|
||||
)
|
||||
.put(
|
||||
"/api/users",
|
||||
"/api/users/metadata",
|
||||
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
||||
controller.update
|
||||
controller.updateMetadata
|
||||
)
|
||||
.post(
|
||||
"/api/users",
|
||||
"/api/users/metadata",
|
||||
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
||||
usage,
|
||||
controller.create
|
||||
controller.createMetadata
|
||||
)
|
||||
.delete(
|
||||
"/api/users/:email",
|
||||
"/api/users/metadata/:email",
|
||||
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
||||
usage,
|
||||
controller.destroy
|
||||
controller.destroyMetadata
|
||||
)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// need to load environment first
|
||||
const env = require("./environment")
|
||||
const CouchDB = require("./db")
|
||||
require("@budibase/auth").init(CouchDB)
|
||||
const Koa = require("koa")
|
||||
const destroyable = require("server-destroy")
|
||||
const electron = require("electron")
|
||||
|
@ -5,7 +9,6 @@ const koaBody = require("koa-body")
|
|||
const logger = require("koa-pino-logger")
|
||||
const http = require("http")
|
||||
const api = require("./api")
|
||||
const env = require("./environment")
|
||||
const eventEmitter = require("./events")
|
||||
const automations = require("./automations/index")
|
||||
const Sentry = require("@sentry/node")
|
||||
|
|
|
@ -75,7 +75,7 @@ module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
|||
request: {
|
||||
body: inputs.row,
|
||||
},
|
||||
user: { appId },
|
||||
appId,
|
||||
eventEmitter: emitter,
|
||||
}
|
||||
|
||||
|
|
|
@ -61,9 +61,7 @@ module.exports.definition = {
|
|||
module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
||||
const { email, password, roleId } = inputs
|
||||
const ctx = {
|
||||
user: {
|
||||
appId: appId,
|
||||
},
|
||||
appId,
|
||||
request: {
|
||||
body: { email, password, roleId },
|
||||
},
|
||||
|
@ -74,11 +72,11 @@ module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
|||
if (env.isProd()) {
|
||||
await usage.update(apiKey, usage.Properties.USER, 1)
|
||||
}
|
||||
await userController.create(ctx)
|
||||
await userController.createMetadata(ctx)
|
||||
return {
|
||||
response: ctx.body,
|
||||
// internal property not returned through the API
|
||||
id: ctx.userId,
|
||||
id: ctx.body._id,
|
||||
revision: ctx.body._rev,
|
||||
success: ctx.status === 200,
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
|||
rowId: inputs.id,
|
||||
revId: inputs.revision,
|
||||
},
|
||||
user: { appId },
|
||||
appId,
|
||||
eventEmitter: emitter,
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ module.exports.run = async function({ inputs, appId, emitter }) {
|
|||
request: {
|
||||
body: inputs.row,
|
||||
},
|
||||
user: { appId },
|
||||
appId,
|
||||
eventEmitter: emitter,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
require("../../environment")
|
||||
const automation = require("../index")
|
||||
const usageQuota = require("../../utilities/usageQuota")
|
||||
const thread = require("../thread")
|
||||
const triggers = require("../triggers")
|
||||
const { basicAutomation, basicTable } = require("../../tests/utilities/structures")
|
||||
const { wait } = require("../../utilities")
|
||||
const env = require("../../environment")
|
||||
const { makePartial } = require("../../tests/utilities")
|
||||
const { cleanInputValues } = require("../automationUtils")
|
||||
const setup = require("./utilities")
|
||||
|
|
|
@ -26,6 +26,7 @@ describe("test the create row action", () => {
|
|||
})
|
||||
expect(res.id).toBeDefined()
|
||||
expect(res.revision).toBeDefined()
|
||||
expect(res.success).toEqual(true)
|
||||
const gottenRow = await config.getRow(table._id, res.id)
|
||||
expect(gottenRow.name).toEqual("test")
|
||||
expect(gottenRow.description).toEqual("test")
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const usageQuota = require("../../utilities/usageQuota")
|
||||
const env = require("../../environment")
|
||||
const setup = require("./utilities")
|
||||
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles")
|
||||
const { ViewNames } = require("../../db/utils")
|
||||
const { InternalTables } = require("../../db/utils")
|
||||
|
||||
jest.mock("../../utilities/usageQuota")
|
||||
|
||||
|
@ -25,8 +24,7 @@ describe("test the create user action", () => {
|
|||
const res = await setup.runStep(setup.actions.CREATE_USER.stepId, user)
|
||||
expect(res.id).toBeDefined()
|
||||
expect(res.revision).toBeDefined()
|
||||
const userDoc = await config.getRow(ViewNames.USERS, res.id)
|
||||
expect(userDoc.email).toEqual(user.email)
|
||||
const userDoc = await config.getRow(InternalTables.USER_METADATA, res.id)
|
||||
})
|
||||
|
||||
it("should return an error if no inputs provided", async () => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
||||
const { UserStatus } = require("@budibase/auth")
|
||||
|
||||
exports.LOGO_URL =
|
||||
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg"
|
||||
|
@ -27,11 +28,6 @@ exports.AuthTypes = {
|
|||
EXTERNAL: "external",
|
||||
}
|
||||
|
||||
exports.UserStatus = {
|
||||
ACTIVE: "active",
|
||||
INACTIVE: "inactive",
|
||||
}
|
||||
|
||||
exports.USERS_TABLE_SCHEMA = {
|
||||
_id: "ta_users",
|
||||
type: "table",
|
||||
|
@ -68,7 +64,7 @@ exports.USERS_TABLE_SCHEMA = {
|
|||
constraints: {
|
||||
type: exports.FieldTypes.STRING,
|
||||
presence: false,
|
||||
inclusion: Object.values(exports.UserStatus),
|
||||
inclusion: Object.values(UserStatus),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -34,7 +34,10 @@ const DocumentTypes = {
|
|||
const ViewNames = {
|
||||
LINK: "by_link",
|
||||
ROUTING: "screen_routes",
|
||||
USERS: "ta_users",
|
||||
}
|
||||
|
||||
const InternalTables = {
|
||||
USER_METADATA: "ta_users",
|
||||
}
|
||||
|
||||
const SearchIndexes = {
|
||||
|
@ -43,6 +46,7 @@ const SearchIndexes = {
|
|||
|
||||
exports.StaticDatabases = StaticDatabases
|
||||
exports.ViewNames = ViewNames
|
||||
exports.InternalTables = InternalTables
|
||||
exports.DocumentTypes = DocumentTypes
|
||||
exports.SEPARATOR = SEPARATOR
|
||||
exports.UNICODE_MAX = UNICODE_MAX
|
||||
|
@ -112,17 +116,19 @@ exports.getRowParams = (tableId = null, rowId = null, otherProps = {}) => {
|
|||
/**
|
||||
* Gets a new row ID for the specified table.
|
||||
* @param {string} tableId The table which the row is being created for.
|
||||
* @param {string|null} id If an ID is to be used then the UUID can be substituted for this.
|
||||
* @returns {string} The new ID which a row doc can be stored under.
|
||||
*/
|
||||
exports.generateRowID = tableId => {
|
||||
return `${DocumentTypes.ROW}${SEPARATOR}${tableId}${SEPARATOR}${newid()}`
|
||||
exports.generateRowID = (tableId, id = null) => {
|
||||
id = id || newid()
|
||||
return `${DocumentTypes.ROW}${SEPARATOR}${tableId}${SEPARATOR}${id}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
||||
*/
|
||||
exports.getUserParams = (email = "", otherProps = {}) => {
|
||||
return exports.getRowParams(ViewNames.USERS, email, otherProps)
|
||||
exports.getUserMetadataParams = (email = "", otherProps = {}) => {
|
||||
return exports.getRowParams(InternalTables.USER_METADATA, email, otherProps)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,8 +136,17 @@ exports.getUserParams = (email = "", otherProps = {}) => {
|
|||
* @param {string} email The email which the ID is going to be built up of.
|
||||
* @returns {string} The new user ID which the user doc can be stored under.
|
||||
*/
|
||||
exports.generateUserID = email => {
|
||||
return `${DocumentTypes.ROW}${SEPARATOR}${ViewNames.USERS}${SEPARATOR}${DocumentTypes.USER}${SEPARATOR}${email}`
|
||||
exports.generateUserMetadataID = email => {
|
||||
return exports.generateRowID(InternalTables.USER_METADATA, email)
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks up the ID to get the email address back out of it.
|
||||
*/
|
||||
exports.getEmailFromUserMetadataID = id => {
|
||||
return id.split(
|
||||
`${DocumentTypes.ROW}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}`
|
||||
)[1]
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
const jwt = require("jsonwebtoken")
|
||||
const STATUS_CODES = require("../utilities/statusCodes")
|
||||
const { getRole, getBuiltinRoles } = require("../utilities/security/roles")
|
||||
const { AuthTypes } = require("../constants")
|
||||
const {
|
||||
getAppId,
|
||||
getCookieName,
|
||||
clearCookie,
|
||||
setCookie,
|
||||
isClient,
|
||||
} = require("../utilities")
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
if (ctx.path === "/builder") {
|
||||
await next()
|
||||
return
|
||||
}
|
||||
|
||||
// do everything we can to make sure the appId is held correctly
|
||||
// we hold it in state as a
|
||||
let appId = getAppId(ctx)
|
||||
const cookieAppId = ctx.cookies.get(getCookieName("currentapp"))
|
||||
const builtinRoles = getBuiltinRoles()
|
||||
if (appId && cookieAppId !== appId) {
|
||||
setCookie(ctx, appId, "currentapp")
|
||||
} else if (cookieAppId) {
|
||||
appId = cookieAppId
|
||||
}
|
||||
let token, authType
|
||||
if (!isClient(ctx)) {
|
||||
token = ctx.cookies.get(getCookieName())
|
||||
authType = AuthTypes.BUILDER
|
||||
}
|
||||
|
||||
if (!token && appId) {
|
||||
token = ctx.cookies.get(getCookieName(appId))
|
||||
authType = AuthTypes.APP
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
ctx.auth.authenticated = false
|
||||
ctx.appId = appId
|
||||
ctx.user = {
|
||||
appId,
|
||||
role: builtinRoles.PUBLIC,
|
||||
}
|
||||
await next()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
ctx.auth.authenticated = authType
|
||||
const jwtPayload = jwt.verify(token, ctx.config.jwtSecret)
|
||||
ctx.appId = appId
|
||||
ctx.auth.apiKey = jwtPayload.apiKey
|
||||
ctx.user = {
|
||||
...jwtPayload,
|
||||
appId: appId,
|
||||
role: await getRole(appId, jwtPayload.roleId),
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
if (authType === AuthTypes.BUILDER) {
|
||||
clearCookie(ctx)
|
||||
ctx.status = 200
|
||||
return
|
||||
} else {
|
||||
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
|
||||
}
|
||||
}
|
||||
|
||||
await next()
|
||||
}
|
|
@ -1,58 +1,41 @@
|
|||
const {
|
||||
BUILTIN_ROLE_IDS,
|
||||
getUserPermissions,
|
||||
} = require("../utilities/security/roles")
|
||||
const { getUserPermissions } = require("../utilities/security/roles")
|
||||
const {
|
||||
PermissionTypes,
|
||||
doesHaveResourcePermission,
|
||||
doesHaveBasePermission,
|
||||
} = require("../utilities/security/permissions")
|
||||
const env = require("../environment")
|
||||
const { isAPIKeyValid } = require("../utilities/security/apikey")
|
||||
const { AuthTypes } = require("../constants")
|
||||
|
||||
const ADMIN_ROLES = [BUILTIN_ROLE_IDS.ADMIN, BUILTIN_ROLE_IDS.BUILDER]
|
||||
|
||||
function hasResource(ctx) {
|
||||
return ctx.resourceId != null
|
||||
}
|
||||
|
||||
module.exports = (permType, permLevel = null) => async (ctx, next) => {
|
||||
if (env.isProd() && ctx.headers["x-api-key"] && ctx.headers["x-instanceid"]) {
|
||||
// api key header passed by external webhook
|
||||
if (await isAPIKeyValid(ctx.headers["x-api-key"])) {
|
||||
ctx.auth = {
|
||||
authenticated: AuthTypes.EXTERNAL,
|
||||
apiKey: ctx.headers["x-api-key"],
|
||||
}
|
||||
ctx.user = {
|
||||
appId: ctx.headers["x-instanceid"],
|
||||
}
|
||||
return next()
|
||||
}
|
||||
const WEBHOOK_ENDPOINTS = new RegExp(
|
||||
["webhooks/trigger", "webhooks/schema"].join("|")
|
||||
)
|
||||
|
||||
return ctx.throw(403, "API key invalid")
|
||||
module.exports = (permType, permLevel = null) => async (ctx, next) => {
|
||||
// webhooks don't need authentication, each webhook unique
|
||||
if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) {
|
||||
return next()
|
||||
}
|
||||
|
||||
if (!ctx.user) {
|
||||
return ctx.throw(403, "No user info found")
|
||||
}
|
||||
|
||||
const role = ctx.user.role
|
||||
const isAdmin = ADMIN_ROLES.includes(role._id)
|
||||
const isAuthed = ctx.auth.authenticated
|
||||
const isAuthed = ctx.isAuthenticated
|
||||
|
||||
const { basePermissions, permissions } = await getUserPermissions(
|
||||
ctx.appId,
|
||||
role._id
|
||||
ctx.roleId
|
||||
)
|
||||
|
||||
// this may need to change in the future, right now only admins
|
||||
// can have access to builder features, this is hard coded into
|
||||
// our rules
|
||||
if (isAdmin && isAuthed) {
|
||||
// builders for now have permission to do anything
|
||||
// TODO: in future should consider separating permissions with an require("@budibase/auth").isClient check
|
||||
let isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
|
||||
if (isBuilder) {
|
||||
return next()
|
||||
} else if (permType === PermissionTypes.BUILDER) {
|
||||
} else if (permType === PermissionTypes.BUILDER && !isBuilder) {
|
||||
return ctx.throw(403, "Not Authorized")
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
const { getAppId, setCookie, getCookie, Cookies } = require("@budibase/auth")
|
||||
const { getRole } = require("../utilities/security/roles")
|
||||
const { generateUserMetadataID } = require("../db/utils")
|
||||
const { getGlobalUsers } = require("../utilities/workerRequests")
|
||||
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
// try to get the appID from the request
|
||||
const requestAppId = getAppId(ctx)
|
||||
// get app cookie if it exists
|
||||
const appCookie = getCookie(ctx, Cookies.CurrentApp)
|
||||
if (!appCookie && !requestAppId) {
|
||||
return next()
|
||||
}
|
||||
|
||||
let updateCookie = false,
|
||||
appId,
|
||||
roleId = BUILTIN_ROLE_IDS.PUBLIC
|
||||
if (!ctx.user) {
|
||||
// not logged in, try to set a cookie for public apps
|
||||
updateCookie = true
|
||||
appId = requestAppId
|
||||
} else if (
|
||||
requestAppId != null &&
|
||||
(appCookie == null ||
|
||||
requestAppId !== appCookie.appId ||
|
||||
appCookie.roleId === BUILTIN_ROLE_IDS.PUBLIC)
|
||||
) {
|
||||
// Different App ID means cookie needs reset, or if the same public user has logged in
|
||||
const globalUser = await getGlobalUsers(ctx, requestAppId, ctx.user.email)
|
||||
updateCookie = true
|
||||
appId = requestAppId
|
||||
if (globalUser.roles && globalUser.roles[requestAppId]) {
|
||||
roleId = globalUser.roles[requestAppId]
|
||||
}
|
||||
} else if (appCookie != null) {
|
||||
appId = appCookie.appId
|
||||
roleId = appCookie.roleId || BUILTIN_ROLE_IDS.PUBLIC
|
||||
}
|
||||
if (appId) {
|
||||
ctx.appId = appId
|
||||
if (roleId) {
|
||||
const userId = ctx.user
|
||||
? generateUserMetadataID(ctx.user.email)
|
||||
: undefined
|
||||
ctx.roleId = roleId
|
||||
ctx.user = {
|
||||
...ctx.user,
|
||||
_id: userId,
|
||||
userId,
|
||||
role: await getRole(appId, roleId),
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updateCookie && appId) {
|
||||
setCookie(ctx, { appId, roleId }, Cookies.CurrentApp)
|
||||
}
|
||||
return next()
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Authenticated middleware sets the correct APP auth type information when the user is not in the builder 1`] = `
|
||||
Object {
|
||||
"apiKey": "1234",
|
||||
"appId": "budibase:app:local",
|
||||
"role": Role {
|
||||
"_id": "ADMIN",
|
||||
"inherits": "POWER",
|
||||
"name": "Admin",
|
||||
"permissionId": "admin",
|
||||
},
|
||||
"roleId": "ADMIN",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Authenticated middleware sets the correct BUILDER auth type information when the x-budibase-type header is not 'client' 1`] = `
|
||||
Object {
|
||||
"apiKey": "1234",
|
||||
"appId": "budibase:builder:local",
|
||||
"role": Role {
|
||||
"_id": "BUILDER",
|
||||
"name": "Builder",
|
||||
"permissionId": "admin",
|
||||
},
|
||||
"roleId": "BUILDER",
|
||||
}
|
||||
`;
|
|
@ -1,124 +0,0 @@
|
|||
const { AuthTypes } = require("../../constants")
|
||||
const authenticatedMiddleware = require("../authenticated")
|
||||
const jwt = require("jsonwebtoken")
|
||||
jest.mock("jsonwebtoken")
|
||||
|
||||
class TestConfiguration {
|
||||
constructor(middleware) {
|
||||
this.middleware = authenticatedMiddleware
|
||||
this.ctx = {
|
||||
config: {},
|
||||
auth: {},
|
||||
cookies: {
|
||||
set: jest.fn(),
|
||||
get: jest.fn(),
|
||||
},
|
||||
headers: {},
|
||||
params: {},
|
||||
path: "",
|
||||
request: {
|
||||
headers: {},
|
||||
},
|
||||
throw: jest.fn(),
|
||||
}
|
||||
this.next = jest.fn()
|
||||
}
|
||||
|
||||
setHeaders(headers) {
|
||||
this.ctx.headers = headers
|
||||
}
|
||||
|
||||
executeMiddleware() {
|
||||
return this.middleware(this.ctx, this.next)
|
||||
}
|
||||
|
||||
afterEach() {
|
||||
jest.resetAllMocks()
|
||||
}
|
||||
}
|
||||
|
||||
describe("Authenticated middleware", () => {
|
||||
let config
|
||||
|
||||
beforeEach(() => {
|
||||
config = new TestConfiguration()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
config.afterEach()
|
||||
})
|
||||
|
||||
it("calls next() when on the builder path", async () => {
|
||||
config.ctx.path = "/builder"
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("sets a new cookie when the current cookie does not match the app id from context", async () => {
|
||||
const appId = "app_123"
|
||||
config.setHeaders({
|
||||
"x-budibase-app-id": appId,
|
||||
})
|
||||
config.ctx.cookies.get.mockImplementation(() => "cookieAppId")
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.cookies.set).toHaveBeenCalledWith(
|
||||
"budibase:currentapp:local",
|
||||
appId,
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("sets the correct BUILDER auth type information when the x-budibase-type header is not 'client'", async () => {
|
||||
config.ctx.cookies.get.mockImplementation(() => "budibase:builder:local")
|
||||
jwt.verify.mockImplementationOnce(() => ({
|
||||
apiKey: "1234",
|
||||
roleId: "BUILDER",
|
||||
}))
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.auth.authenticated).toEqual(AuthTypes.BUILDER)
|
||||
expect(config.ctx.user).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("sets the correct APP auth type information when the user is not in the builder", async () => {
|
||||
config.setHeaders({
|
||||
"x-budibase-type": "client",
|
||||
})
|
||||
config.ctx.cookies.get.mockImplementation(() => `budibase:app:local`)
|
||||
jwt.verify.mockImplementationOnce(() => ({
|
||||
apiKey: "1234",
|
||||
roleId: "ADMIN",
|
||||
}))
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.auth.authenticated).toEqual(AuthTypes.APP)
|
||||
expect(config.ctx.user).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("marks the user as unauthenticated when a token cannot be determined from the users cookie", async () => {
|
||||
config.executeMiddleware()
|
||||
expect(config.ctx.auth.authenticated).toBe(false)
|
||||
expect(config.ctx.user.role).toEqual({
|
||||
_id: "PUBLIC",
|
||||
name: "Public",
|
||||
permissionId: "public",
|
||||
})
|
||||
})
|
||||
|
||||
it("clears the cookie when there is an error authenticating in the builder", async () => {
|
||||
config.ctx.cookies.get.mockImplementation(() => "budibase:builder:local")
|
||||
jwt.verify.mockImplementationOnce(() => {
|
||||
throw new Error()
|
||||
})
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.ctx.cookies.set).toBeCalledWith("budibase:builder:local")
|
||||
})
|
||||
})
|
|
@ -1,7 +1,5 @@
|
|||
const authorizedMiddleware = require("../authorized")
|
||||
const env = require("../../environment")
|
||||
const apiKey = require("../../utilities/security/apikey")
|
||||
const { AuthTypes } = require("../../constants")
|
||||
const { PermissionTypes, PermissionLevels } = require("../../utilities/security/permissions")
|
||||
jest.mock("../../environment", () => ({
|
||||
prod: false,
|
||||
|
@ -12,7 +10,6 @@ jest.mock("../../environment", () => ({
|
|||
}
|
||||
})
|
||||
)
|
||||
jest.mock("../../utilities/security/apikey")
|
||||
|
||||
class TestConfiguration {
|
||||
constructor(role) {
|
||||
|
@ -92,29 +89,6 @@ describe("Authorization middleware", () => {
|
|||
"x-instanceid": "instance123",
|
||||
})
|
||||
})
|
||||
|
||||
it("passes to next() if api key is valid", async () => {
|
||||
apiKey.isAPIKeyValid.mockResolvedValueOnce(true)
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
expect(config.ctx.auth).toEqual({
|
||||
authenticated: AuthTypes.EXTERNAL,
|
||||
apiKey: config.ctx.headers["x-api-key"],
|
||||
})
|
||||
expect(config.ctx.user).toEqual({
|
||||
appId: config.ctx.headers["x-instanceid"],
|
||||
})
|
||||
})
|
||||
|
||||
it("throws if api key is invalid", async () => {
|
||||
apiKey.isAPIKeyValid.mockResolvedValueOnce(false)
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.throw).toHaveBeenCalledWith(403, "API key invalid")
|
||||
})
|
||||
})
|
||||
|
||||
describe("non-webhook call", () => {
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
mockAuthWithNoCookie()
|
||||
mockWorker()
|
||||
|
||||
function mockWorker() {
|
||||
jest.mock("../../utilities/workerRequests", () => ({
|
||||
getGlobalUsers: () => {
|
||||
return {
|
||||
email: "test@test.com",
|
||||
roles: {
|
||||
"app_test": "BASIC",
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
function mockReset() {
|
||||
jest.resetModules()
|
||||
mockWorker()
|
||||
}
|
||||
|
||||
function mockAuthWithNoCookie() {
|
||||
jest.resetModules()
|
||||
mockWorker()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: jest.fn(),
|
||||
setCookie: jest.fn(),
|
||||
getCookie: jest.fn(),
|
||||
Cookies: {},
|
||||
}))
|
||||
}
|
||||
|
||||
function mockAuthWithCookie() {
|
||||
jest.resetModules()
|
||||
mockWorker()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: () => {
|
||||
return "app_test"
|
||||
},
|
||||
setCookie: jest.fn(),
|
||||
getCookie: () => ({ appId: "app_different", roleId: "PUBLIC" }),
|
||||
Cookies: {
|
||||
Auth: "auth",
|
||||
CurrentApp: "currentapp",
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
class TestConfiguration {
|
||||
constructor() {
|
||||
this.next = jest.fn()
|
||||
this.throw = jest.fn()
|
||||
|
||||
this.ctx = {
|
||||
next: this.next,
|
||||
throw: this.throw
|
||||
}
|
||||
}
|
||||
|
||||
setUser() {
|
||||
this.ctx.user = {
|
||||
email: "test@test.com",
|
||||
}
|
||||
}
|
||||
|
||||
executeMiddleware() {
|
||||
// import as late as possible for mocks
|
||||
const currentAppMiddleware = require("../currentapp")
|
||||
return currentAppMiddleware(this.ctx, this.next)
|
||||
}
|
||||
}
|
||||
|
||||
describe("Current app middleware", () => {
|
||||
let config
|
||||
|
||||
beforeEach(() => {
|
||||
config = new TestConfiguration()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("test having no cookies or app ID", () => {
|
||||
it("should be able to proceed with nothing setup", async () => {
|
||||
await config.executeMiddleware()
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("check get public for app when not logged in", () => {
|
||||
it("should be able to proceed with no login, but cookies configured", async () => {
|
||||
mockAuthWithCookie()
|
||||
await config.executeMiddleware()
|
||||
expect(config.ctx.roleId).toEqual("PUBLIC")
|
||||
expect(config.ctx.appId).toEqual("app_test")
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("check functionality when logged in", () => {
|
||||
async function checkExpected(setCookie) {
|
||||
config.setUser()
|
||||
await config.executeMiddleware()
|
||||
const cookieFn = require("@budibase/auth").setCookie
|
||||
if (setCookie) {
|
||||
expect(cookieFn).toHaveBeenCalled()
|
||||
} else {
|
||||
expect(cookieFn).not.toHaveBeenCalled()
|
||||
}
|
||||
expect(config.ctx.roleId).toEqual("BASIC")
|
||||
expect(config.ctx.user.role._id).toEqual("BASIC")
|
||||
expect(config.ctx.appId).toEqual("app_test")
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
}
|
||||
|
||||
it("should be able to setup an app token when cookie not setup", async () => {
|
||||
mockAuthWithCookie()
|
||||
await checkExpected(true)
|
||||
})
|
||||
|
||||
it("should perform correct when no cookie exists", async () => {
|
||||
mockReset()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: () => {
|
||||
return "app_test"
|
||||
},
|
||||
setCookie: jest.fn(),
|
||||
getCookie: jest.fn(),
|
||||
Cookies: {},
|
||||
}))
|
||||
await checkExpected(true)
|
||||
})
|
||||
|
||||
it("lastly check what occurs when cookie doesn't need updated", async () => {
|
||||
mockReset()
|
||||
jest.mock("@budibase/auth", () => ({
|
||||
getAppId: () => {
|
||||
return "app_test"
|
||||
},
|
||||
setCookie: jest.fn(),
|
||||
getCookie: () => ({ appId: "app_test", roleId: "BASIC" }),
|
||||
Cookies: {},
|
||||
}))
|
||||
await checkExpected(false)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
|
@ -20,9 +20,7 @@ class TestConfiguration {
|
|||
this.ctx = {
|
||||
throw: this.throw,
|
||||
next: this.next,
|
||||
user: {
|
||||
appId: "test"
|
||||
},
|
||||
appId: "test",
|
||||
request: {
|
||||
body: {}
|
||||
},
|
||||
|
@ -78,8 +76,10 @@ describe("usageQuota middleware", () => {
|
|||
})
|
||||
|
||||
it("passes through to next middleware if document already exists", async () => {
|
||||
config.setProd(true)
|
||||
config.setBody({
|
||||
_id: "test"
|
||||
_id: "test",
|
||||
_rev: "test",
|
||||
})
|
||||
|
||||
CouchDB.mockImplementationOnce(() => ({
|
||||
|
@ -89,13 +89,14 @@ describe("usageQuota middleware", () => {
|
|||
await config.executeMiddleware()
|
||||
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
expect(config.ctx.preExisting).toBe(true)
|
||||
})
|
||||
|
||||
it("throws if request has _id, but the document no longer exists", async () => {
|
||||
config.setBody({
|
||||
_id: "123"
|
||||
_id: "123",
|
||||
_rev: "test",
|
||||
})
|
||||
config.setProd(true)
|
||||
|
||||
CouchDB.mockImplementationOnce(() => ({
|
||||
get: async () => {
|
||||
|
|
|
@ -27,27 +27,27 @@ function getProperty(url) {
|
|||
}
|
||||
|
||||
module.exports = async (ctx, next) => {
|
||||
const db = new CouchDB(ctx.user.appId)
|
||||
// if in development or a self hosted cloud usage quotas should not be executed
|
||||
if (env.isDev() || env.SELF_HOSTED) {
|
||||
return next()
|
||||
}
|
||||
|
||||
const db = new CouchDB(ctx.appId)
|
||||
let usage = METHOD_MAP[ctx.req.method]
|
||||
const property = getProperty(ctx.req.url)
|
||||
if (usage == null || property == null) {
|
||||
return next()
|
||||
}
|
||||
// post request could be a save of a pre-existing entry
|
||||
if (ctx.request.body && ctx.request.body._id) {
|
||||
if (ctx.request.body && ctx.request.body._id && ctx.request.body._rev) {
|
||||
try {
|
||||
ctx.preExisting = await db.get(ctx.request.body._id)
|
||||
await db.get(ctx.request.body._id)
|
||||
return next()
|
||||
} catch (err) {
|
||||
ctx.throw(404, `${ctx.request.body._id} does not exist`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// if in development or a self hosted cloud usage quotas should not be executed
|
||||
if (env.isDev() || env.SELF_HOSTED) {
|
||||
return next()
|
||||
}
|
||||
// update usage for uploads to be the total size
|
||||
if (property === usageQuota.Properties.UPLOAD) {
|
||||
const files =
|
||||
|
|
|
@ -15,6 +15,7 @@ const {
|
|||
const controllers = require("./controllers")
|
||||
const supertest = require("supertest")
|
||||
const { cleanup } = require("../../utilities/fileSystem")
|
||||
const { Cookies } = require("@budibase/auth")
|
||||
|
||||
const EMAIL = "babs@babs.com"
|
||||
const PASSWORD = "babs_password"
|
||||
|
@ -68,16 +69,25 @@ class TestConfiguration {
|
|||
}
|
||||
|
||||
defaultHeaders() {
|
||||
const builderUser = {
|
||||
userId: "BUILDER",
|
||||
roleId: BUILTIN_ROLE_IDS.BUILDER,
|
||||
const user = {
|
||||
userId: "us_test@test.com",
|
||||
email: "test@test.com",
|
||||
builder: {
|
||||
global: true,
|
||||
},
|
||||
}
|
||||
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
|
||||
// can be "production" for test case
|
||||
const type = env.isProd() ? "cloud" : "local"
|
||||
const app = {
|
||||
roleId: BUILTIN_ROLE_IDS.BUILDER,
|
||||
appId: this.appId,
|
||||
}
|
||||
const authToken = jwt.sign(user, env.JWT_SECRET)
|
||||
const appToken = jwt.sign(app, env.JWT_SECRET)
|
||||
const headers = {
|
||||
Accept: "application/json",
|
||||
Cookie: [`budibase:builder:${type}=${builderToken}`],
|
||||
Cookie: [
|
||||
`${Cookies.Auth}=${authToken}`,
|
||||
`${Cookies.CurrentApp}=${appToken}`,
|
||||
],
|
||||
}
|
||||
if (this.appId) {
|
||||
headers["x-budibase-app-id"] = this.appId
|
||||
|
@ -101,7 +111,7 @@ class TestConfiguration {
|
|||
} catch (err) {
|
||||
// allow errors here
|
||||
}
|
||||
return this.login(email, PASSWORD)
|
||||
return this.login(email, PASSWORD, roleId)
|
||||
}
|
||||
|
||||
async createApp(appName) {
|
||||
|
@ -279,7 +289,7 @@ class TestConfiguration {
|
|||
roleId,
|
||||
},
|
||||
null,
|
||||
controllers.user.create
|
||||
controllers.user.createMetadata
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -289,7 +299,7 @@ class TestConfiguration {
|
|||
{
|
||||
email,
|
||||
},
|
||||
controllers.user.find
|
||||
controllers.user.findMetadata
|
||||
)
|
||||
return this._req(
|
||||
{
|
||||
|
@ -297,30 +307,35 @@ class TestConfiguration {
|
|||
status: "inactive",
|
||||
},
|
||||
null,
|
||||
controllers.user.update
|
||||
controllers.user.updateMetadata
|
||||
)
|
||||
}
|
||||
|
||||
async login(email, password) {
|
||||
async login(email, password, roleId = BUILTIN_ROLE_IDS.BUILDER) {
|
||||
if (!this.request) {
|
||||
throw "Server has not been opened, cannot login."
|
||||
}
|
||||
if (!email || !password) {
|
||||
await this.createUser()
|
||||
email = EMAIL
|
||||
password = PASSWORD
|
||||
}
|
||||
const result = await this.request
|
||||
.post(`/api/authenticate`)
|
||||
.set({
|
||||
"x-budibase-app-id": this.appId,
|
||||
})
|
||||
.send({ email, password })
|
||||
const user = {
|
||||
userId: `us_${email || EMAIL}`,
|
||||
email: email || EMAIL,
|
||||
}
|
||||
const app = {
|
||||
roleId: roleId,
|
||||
appId: this.appId,
|
||||
}
|
||||
const authToken = jwt.sign(user, env.JWT_SECRET)
|
||||
const appToken = jwt.sign(app, env.JWT_SECRET)
|
||||
|
||||
// returning necessary request headers
|
||||
return {
|
||||
Accept: "application/json",
|
||||
Cookie: result.headers["set-cookie"],
|
||||
Cookie: [
|
||||
`${Cookies.Auth}=${authToken}`,
|
||||
`${Cookies.CurrentApp}=${appToken}`,
|
||||
],
|
||||
"x-budibase-app-id": this.appId,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// TODO: REMOVE
|
||||
|
||||
const bcrypt = require("bcryptjs")
|
||||
const env = require("../environment")
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue