simplify user authentication, remove anon user, fix login cookie issues
This commit is contained in:
parent
f921f4c090
commit
e95af51cde
|
@ -27,7 +27,6 @@ context("Create a Table", () => {
|
||||||
cy.get(".actions input")
|
cy.get(".actions input")
|
||||||
.first()
|
.first()
|
||||||
.type("updated")
|
.type("updated")
|
||||||
cy.get("select").select("Text")
|
|
||||||
cy.contains("Save Column").click()
|
cy.contains("Save Column").click()
|
||||||
cy.contains("nameupdated").should("have.text", "nameupdated")
|
cy.contains("nameupdated").should("have.text", "nameupdated")
|
||||||
})
|
})
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
<Input thin bind:value={username} name="Name" placeholder="Username" />
|
<Input thin bind:value={username} name="Name" placeholder="Username" />
|
||||||
<Input
|
<Input
|
||||||
thin
|
thin
|
||||||
|
type="password"
|
||||||
bind:value={password}
|
bind:value={password}
|
||||||
name="Password"
|
name="Password"
|
||||||
placeholder="Password" />
|
placeholder="Password" />
|
||||||
|
|
|
@ -68,7 +68,10 @@
|
||||||
<span
|
<span
|
||||||
class:active={false}
|
class:active={false}
|
||||||
class="topnavitemright"
|
class="topnavitemright"
|
||||||
on:click={() => window.open(`/${application}`)}>
|
on:click={() => {
|
||||||
|
document.cookie = 'budibase:token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'
|
||||||
|
window.open(`/${application}`)
|
||||||
|
}}>
|
||||||
<PreviewIcon />
|
<PreviewIcon />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,7 +80,7 @@
|
||||||
{#await promise}
|
{#await promise}
|
||||||
<!-- This should probably be some kind of loading state? -->
|
<!-- This should probably be some kind of loading state? -->
|
||||||
<div />
|
<div />
|
||||||
{:then result}
|
{:then results}
|
||||||
<slot />
|
<slot />
|
||||||
{:catch error}
|
{:catch error}
|
||||||
<p>Something went wrong: {error.message}</p>
|
<p>Something went wrong: {error.message}</p>
|
||||||
|
|
|
@ -41,7 +41,7 @@ exports.authenticate = async ctx => {
|
||||||
dbUser = await instanceDb.get(generateUserID(username))
|
dbUser = await instanceDb.get(generateUserID(username))
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// do not want to throw a 404 - as this could be
|
// do not want to throw a 404 - as this could be
|
||||||
// used to dtermine valid usernames
|
// used to determine valid usernames
|
||||||
ctx.throw(401, "Invalid Credentials")
|
ctx.throw(401, "Invalid Credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const send = require("koa-send")
|
const send = require("koa-send")
|
||||||
const { resolve, join } = require("../../utilities/centralPath")
|
const { resolve, join } = require("../../utilities/centralPath")
|
||||||
const jwt = require("jsonwebtoken")
|
|
||||||
const fetch = require("node-fetch")
|
const fetch = require("node-fetch")
|
||||||
const fs = require("fs-extra")
|
const fs = require("fs-extra")
|
||||||
const uuid = require("uuid")
|
const uuid = require("uuid")
|
||||||
|
@ -13,8 +12,8 @@ const {
|
||||||
} = require("../../utilities/budibaseDir")
|
} = require("../../utilities/budibaseDir")
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
|
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
|
||||||
const { ANON_LEVEL_ID } = require("../../utilities/accessLevels")
|
|
||||||
const fileProcessor = require("../../utilities/fileProcessor")
|
const fileProcessor = require("../../utilities/fileProcessor")
|
||||||
|
const { AuthTypes } = require("../../constants")
|
||||||
|
|
||||||
exports.serveBuilder = async function(ctx) {
|
exports.serveBuilder = async function(ctx) {
|
||||||
let builderPath = resolve(__dirname, "../../../builder")
|
let builderPath = resolve(__dirname, "../../../builder")
|
||||||
|
@ -136,7 +135,8 @@ exports.performLocalFileProcessing = async function(ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.serveApp = async function(ctx) {
|
exports.serveApp = async function(ctx) {
|
||||||
const mainOrAuth = ctx.auth.authenticated ? "main" : "unauthenticated"
|
const mainOrAuth =
|
||||||
|
ctx.auth.authenticated === AuthTypes.APP ? "main" : "unauthenticated"
|
||||||
|
|
||||||
// default to homedir
|
// default to homedir
|
||||||
const appPath = resolve(
|
const appPath = resolve(
|
||||||
|
@ -146,26 +146,7 @@ exports.serveApp = async function(ctx) {
|
||||||
mainOrAuth
|
mainOrAuth
|
||||||
)
|
)
|
||||||
|
|
||||||
let appId = ctx.params.appId
|
const appId = ctx.user.appId
|
||||||
if (process.env.CLOUD) {
|
|
||||||
appId = ctx.subdomains[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// only set the appId cookie for /appId .. we COULD check for valid appIds
|
|
||||||
// but would like to avoid that DB hit
|
|
||||||
const looksLikeAppId = /^(app_)?[0-9a-f]{32}$/.test(appId)
|
|
||||||
if (looksLikeAppId && !ctx.auth.authenticated) {
|
|
||||||
const anonUser = {
|
|
||||||
userId: "ANON",
|
|
||||||
accessLevelId: ANON_LEVEL_ID,
|
|
||||||
appId,
|
|
||||||
}
|
|
||||||
const anonToken = jwt.sign(anonUser, ctx.config.jwtSecret)
|
|
||||||
ctx.cookies.set("budibase:token", anonToken, {
|
|
||||||
path: "/",
|
|
||||||
httpOnly: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.CLOUD) {
|
if (process.env.CLOUD) {
|
||||||
const S3_URL = `https://${appId}.app.budi.live/assets/${appId}/${mainOrAuth}/${ctx.file ||
|
const S3_URL = `https://${appId}.app.budi.live/assets/${appId}/${mainOrAuth}/${ctx.file ||
|
||||||
|
@ -200,7 +181,8 @@ exports.serveAttachment = async function(ctx) {
|
||||||
|
|
||||||
exports.serveAppAsset = async function(ctx) {
|
exports.serveAppAsset = async function(ctx) {
|
||||||
// default to homedir
|
// default to homedir
|
||||||
const mainOrAuth = ctx.auth.authenticated ? "main" : "unauthenticated"
|
const mainOrAuth =
|
||||||
|
ctx.auth.authenticated === AuthTypes.APP ? "main" : "unauthenticated"
|
||||||
|
|
||||||
const appPath = resolve(
|
const appPath = resolve(
|
||||||
budibaseAppsDir(),
|
budibaseAppsDir(),
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
const AuthTypes = {
|
||||||
|
APP: "app",
|
||||||
|
BUILDER: "builder",
|
||||||
|
EXTERNAL: "external",
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.AuthTypes = AuthTypes
|
|
@ -7,6 +7,8 @@ const {
|
||||||
BUILDER_LEVEL_ID,
|
BUILDER_LEVEL_ID,
|
||||||
ANON_LEVEL_ID,
|
ANON_LEVEL_ID,
|
||||||
} = require("../utilities/accessLevels")
|
} = require("../utilities/accessLevels")
|
||||||
|
const environment = require("../environment")
|
||||||
|
const { AuthTypes } = require("../constants")
|
||||||
|
|
||||||
module.exports = async (ctx, next) => {
|
module.exports = async (ctx, next) => {
|
||||||
if (ctx.path === "/_builder") {
|
if (ctx.path === "/_builder") {
|
||||||
|
@ -17,36 +19,28 @@ module.exports = async (ctx, next) => {
|
||||||
const appToken = ctx.cookies.get("budibase:token")
|
const appToken = ctx.cookies.get("budibase:token")
|
||||||
const builderToken = ctx.cookies.get("builder:token")
|
const builderToken = ctx.cookies.get("builder:token")
|
||||||
|
|
||||||
if (builderToken) {
|
let token
|
||||||
try {
|
// if running locally in the builder itself
|
||||||
const jwtPayload = jwt.verify(builderToken, ctx.config.jwtSecret)
|
if (!environment.CLOUD && !appToken) {
|
||||||
ctx.auth = {
|
token = builderToken
|
||||||
apiKey: jwtPayload.apiKey,
|
ctx.auth.authenticated = AuthTypes.BUILDER
|
||||||
authenticated: jwtPayload.accessLevelId === BUILDER_LEVEL_ID,
|
} else {
|
||||||
}
|
token = appToken
|
||||||
ctx.user = {
|
ctx.auth.authenticated = AuthTypes.APP
|
||||||
...jwtPayload,
|
|
||||||
accessLevel: await getAccessLevel(
|
|
||||||
jwtPayload.instanceId,
|
|
||||||
jwtPayload.accessLevelId
|
|
||||||
),
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
// empty: do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
await next()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appToken) {
|
if (!token) {
|
||||||
ctx.auth.authenticated = false
|
ctx.auth.authenticated = false
|
||||||
|
ctx.user = {
|
||||||
|
appId: process.env.CLOUD ? ctx.subdomains[1] : ctx.params.appId,
|
||||||
|
}
|
||||||
await next()
|
await next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const jwtPayload = jwt.verify(appToken, ctx.config.jwtSecret)
|
const jwtPayload = jwt.verify(token, ctx.config.jwtSecret)
|
||||||
|
ctx.auth.apiKey = jwtPayload.apiKey
|
||||||
ctx.user = {
|
ctx.user = {
|
||||||
...jwtPayload,
|
...jwtPayload,
|
||||||
accessLevel: await getAccessLevel(
|
accessLevel: await getAccessLevel(
|
||||||
|
@ -54,10 +48,6 @@ module.exports = async (ctx, next) => {
|
||||||
jwtPayload.accessLevelId
|
jwtPayload.accessLevelId
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
ctx.auth = {
|
|
||||||
authenticated: ctx.user.accessLevelId !== ANON_LEVEL_ID,
|
|
||||||
apiKey: jwtPayload.apiKey,
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
|
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ const {
|
||||||
} = require("../utilities/accessLevels")
|
} = require("../utilities/accessLevels")
|
||||||
const environment = require("../environment")
|
const environment = require("../environment")
|
||||||
const { apiKeyTable } = require("../db/dynamoClient")
|
const { apiKeyTable } = require("../db/dynamoClient")
|
||||||
|
const { AuthTypes } = require("../constants")
|
||||||
|
|
||||||
module.exports = (permName, getItemId) => async (ctx, next) => {
|
module.exports = (permName, getItemId) => async (ctx, next) => {
|
||||||
if (
|
if (
|
||||||
|
@ -21,8 +22,7 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
|
||||||
|
|
||||||
if (apiKeyInfo) {
|
if (apiKeyInfo) {
|
||||||
ctx.auth = {
|
ctx.auth = {
|
||||||
authenticated: true,
|
authenticated: AuthTypes.EXTERNAL,
|
||||||
external: true,
|
|
||||||
apiKey: ctx.headers["x-api-key"],
|
apiKey: ctx.headers["x-api-key"],
|
||||||
}
|
}
|
||||||
ctx.user = {
|
ctx.user = {
|
||||||
|
@ -42,6 +42,10 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
|
||||||
ctx.throw(403, "User not found")
|
ctx.throw(403, "User not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx.user.accessLevel._id === BUILDER_LEVEL_ID) {
|
if (ctx.user.accessLevel._id === BUILDER_LEVEL_ID) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
@ -53,10 +57,6 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
|
||||||
|
|
||||||
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
|
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
|
||||||
|
|
||||||
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
const thisPermissionId = permissionId({
|
const thisPermissionId = permissionId({
|
||||||
name: permName,
|
name: permName,
|
||||||
itemId: getItemId && getItemId(ctx),
|
itemId: getItemId && getItemId(ctx),
|
||||||
|
|
|
@ -21,7 +21,6 @@ module.exports.PRETTY_ACCESS_LEVELS = {
|
||||||
[module.exports.ADMIN_LEVEL_ID]: "Admin",
|
[module.exports.ADMIN_LEVEL_ID]: "Admin",
|
||||||
[module.exports.POWERUSER_LEVEL_ID]: "Power user",
|
[module.exports.POWERUSER_LEVEL_ID]: "Power user",
|
||||||
[module.exports.BUILDER_LEVEL_ID]: "Builder",
|
[module.exports.BUILDER_LEVEL_ID]: "Builder",
|
||||||
[module.exports.ANON_LEVEL_ID]: "Anonymous",
|
|
||||||
}
|
}
|
||||||
module.exports.adminPermissions = [
|
module.exports.adminPermissions = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
{#await _appPromise}
|
{#await _appPromise}
|
||||||
loading
|
loading
|
||||||
{:then _bb}
|
{:then [object Object]}
|
||||||
<div id="current_component" bind:this={currentComponent} />
|
<div id="current_component" bind:this={currentComponent} />
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue