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")
.first()
.type("updated")
cy.get("select").select("Text")
cy.contains("Save Column").click()
cy.contains("nameupdated").should("have.text", "nameupdated")
})

View File

@ -58,6 +58,7 @@
<Input thin bind:value={username} name="Name" placeholder="Username" />
<Input
thin
type="password"
bind:value={password}
name="Password"
placeholder="Password" />

View File

@ -68,7 +68,10 @@
<span
class:active={false}
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 />
</span>
</div>
@ -77,7 +80,7 @@
{#await promise}
<!-- This should probably be some kind of loading state? -->
<div />
{:then result}
{:then results}
<slot />
{:catch error}
<p>Something went wrong: {error.message}</p>

View File

@ -41,7 +41,7 @@ exports.authenticate = async ctx => {
dbUser = await instanceDb.get(generateUserID(username))
} catch (_) {
// 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")
}

View File

@ -1,6 +1,5 @@
const send = require("koa-send")
const { resolve, join } = require("../../utilities/centralPath")
const jwt = require("jsonwebtoken")
const fetch = require("node-fetch")
const fs = require("fs-extra")
const uuid = require("uuid")
@ -13,8 +12,8 @@ const {
} = require("../../utilities/budibaseDir")
const CouchDB = require("../../db")
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
const { ANON_LEVEL_ID } = require("../../utilities/accessLevels")
const fileProcessor = require("../../utilities/fileProcessor")
const { AuthTypes } = require("../../constants")
exports.serveBuilder = async function(ctx) {
let builderPath = resolve(__dirname, "../../../builder")
@ -136,7 +135,8 @@ exports.performLocalFileProcessing = 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
const appPath = resolve(
@ -146,26 +146,7 @@ exports.serveApp = async function(ctx) {
mainOrAuth
)
let appId = ctx.params.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,
})
}
const appId = ctx.user.appId
if (process.env.CLOUD) {
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) {
// default to homedir
const mainOrAuth = ctx.auth.authenticated ? "main" : "unauthenticated"
const mainOrAuth =
ctx.auth.authenticated === AuthTypes.APP ? "main" : "unauthenticated"
const appPath = resolve(
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,
ANON_LEVEL_ID,
} = require("../utilities/accessLevels")
const environment = require("../environment")
const { AuthTypes } = require("../constants")
module.exports = async (ctx, next) => {
if (ctx.path === "/_builder") {
@ -17,36 +19,28 @@ module.exports = async (ctx, next) => {
const appToken = ctx.cookies.get("budibase:token")
const builderToken = ctx.cookies.get("builder:token")
if (builderToken) {
try {
const jwtPayload = jwt.verify(builderToken, ctx.config.jwtSecret)
ctx.auth = {
apiKey: jwtPayload.apiKey,
authenticated: jwtPayload.accessLevelId === BUILDER_LEVEL_ID,
}
ctx.user = {
...jwtPayload,
accessLevel: await getAccessLevel(
jwtPayload.instanceId,
jwtPayload.accessLevelId
),
}
} catch (_) {
// empty: do nothing
}
await next()
return
let token
// if running locally in the builder itself
if (!environment.CLOUD && !appToken) {
token = builderToken
ctx.auth.authenticated = AuthTypes.BUILDER
} else {
token = appToken
ctx.auth.authenticated = AuthTypes.APP
}
if (!appToken) {
if (!token) {
ctx.auth.authenticated = false
ctx.user = {
appId: process.env.CLOUD ? ctx.subdomains[1] : ctx.params.appId,
}
await next()
return
}
try {
const jwtPayload = jwt.verify(appToken, ctx.config.jwtSecret)
const jwtPayload = jwt.verify(token, ctx.config.jwtSecret)
ctx.auth.apiKey = jwtPayload.apiKey
ctx.user = {
...jwtPayload,
accessLevel: await getAccessLevel(
@ -54,10 +48,6 @@ module.exports = async (ctx, next) => {
jwtPayload.accessLevelId
),
}
ctx.auth = {
authenticated: ctx.user.accessLevelId !== ANON_LEVEL_ID,
apiKey: jwtPayload.apiKey,
}
} catch (err) {
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
}

View File

@ -7,6 +7,7 @@ const {
} = require("../utilities/accessLevels")
const environment = require("../environment")
const { apiKeyTable } = require("../db/dynamoClient")
const { AuthTypes } = require("../constants")
module.exports = (permName, getItemId) => async (ctx, next) => {
if (
@ -21,8 +22,7 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
if (apiKeyInfo) {
ctx.auth = {
authenticated: true,
external: true,
authenticated: AuthTypes.EXTERNAL,
apiKey: ctx.headers["x-api-key"],
}
ctx.user = {
@ -42,6 +42,10 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
ctx.throw(403, "User not found")
}
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
return next()
}
if (ctx.user.accessLevel._id === BUILDER_LEVEL_ID) {
return next()
}
@ -53,10 +57,6 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
return next()
}
const thisPermissionId = permissionId({
name: permName,
itemId: getItemId && getItemId(ctx),

View File

@ -21,7 +21,6 @@ module.exports.PRETTY_ACCESS_LEVELS = {
[module.exports.ADMIN_LEVEL_ID]: "Admin",
[module.exports.POWERUSER_LEVEL_ID]: "Power user",
[module.exports.BUILDER_LEVEL_ID]: "Builder",
[module.exports.ANON_LEVEL_ID]: "Anonymous",
}
module.exports.adminPermissions = [
{

View File

@ -35,7 +35,7 @@
{#await _appPromise}
loading
{:then _bb}
{:then [object Object]}
<div id="current_component" bind:this={currentComponent} />
{/await}