1
0
Fork 0
mirror of synced 2024-09-28 15:21:28 +12:00

simplify user authentication, remove anon user, fix login cookie issues

This commit is contained in:
Martin McKeaveney 2020-10-13 21:33:56 +01:00
parent f921f4c090
commit e95af51cde
10 changed files with 43 additions and 62 deletions

View file

@ -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")
}) })

View file

@ -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" />

View file

@ -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>

View file

@ -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")
} }

View file

@ -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(),

View file

@ -0,0 +1,7 @@
const AuthTypes = {
APP: "app",
BUILDER: "builder",
EXTERNAL: "external",
}
exports.AuthTypes = AuthTypes

View file

@ -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() if (!token) {
return
}
if (!appToken) {
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)
} }

View file

@ -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),

View file

@ -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 = [
{ {

View file

@ -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}