Merge pull request #1781 from Budibase/fix/mike-fixes
Fixes and making login/forgot/reset password pages respect logo and company name
This commit is contained in:
commit
abc5a6687c
|
@ -22,6 +22,12 @@ function buildNoAuthRegex(patterns) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function finalise(ctx, { authenticated, user, internal } = {}) {
|
||||||
|
ctx.isAuthenticated = authenticated || false
|
||||||
|
ctx.user = user
|
||||||
|
ctx.internal = internal || false
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = (noAuthPatterns = [], opts) => {
|
module.exports = (noAuthPatterns = [], opts) => {
|
||||||
const noAuthOptions = noAuthPatterns ? buildNoAuthRegex(noAuthPatterns) : []
|
const noAuthOptions = noAuthPatterns ? buildNoAuthRegex(noAuthPatterns) : []
|
||||||
return async (ctx, next) => {
|
return async (ctx, next) => {
|
||||||
|
@ -36,35 +42,39 @@ module.exports = (noAuthPatterns = [], opts) => {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const apiKey = ctx.request.headers["x-budibase-api-key"]
|
|
||||||
// check the actual user is authenticated first
|
// check the actual user is authenticated first
|
||||||
const authCookie = getCookie(ctx, Cookies.Auth)
|
const authCookie = getCookie(ctx, Cookies.Auth)
|
||||||
|
let authenticated = false,
|
||||||
// this is an internal request, no user made it
|
user = null,
|
||||||
if (apiKey && apiKey === env.INTERNAL_API_KEY) {
|
internal = false
|
||||||
ctx.isAuthenticated = true
|
if (authCookie) {
|
||||||
ctx.internal = true
|
|
||||||
} else if (authCookie) {
|
|
||||||
try {
|
try {
|
||||||
const db = database.getDB(StaticDatabases.GLOBAL.name)
|
const db = database.getDB(StaticDatabases.GLOBAL.name)
|
||||||
const user = await db.get(authCookie.userId)
|
user = await db.get(authCookie.userId)
|
||||||
delete user.password
|
delete user.password
|
||||||
ctx.isAuthenticated = true
|
authenticated = true
|
||||||
ctx.user = user
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// remove the cookie as the use does not exist anymore
|
// remove the cookie as the use does not exist anymore
|
||||||
clearCookie(ctx, Cookies.Auth)
|
clearCookie(ctx, Cookies.Auth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// be explicit
|
const apiKey = ctx.request.headers["x-budibase-api-key"]
|
||||||
if (ctx.isAuthenticated !== true) {
|
// this is an internal request, no user made it
|
||||||
ctx.isAuthenticated = false
|
if (!authenticated && apiKey && apiKey === env.INTERNAL_API_KEY) {
|
||||||
|
authenticated = true
|
||||||
|
internal = true
|
||||||
}
|
}
|
||||||
|
// be explicit
|
||||||
|
if (authenticated !== true) {
|
||||||
|
authenticated = false
|
||||||
|
}
|
||||||
|
// isAuthenticated is a function, so use a variable to be able to check authed state
|
||||||
|
finalise(ctx, { authenticated, user, internal })
|
||||||
return next()
|
return next()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// allow configuring for public access
|
// allow configuring for public access
|
||||||
if (opts && opts.publicAllowed) {
|
if (opts && opts.publicAllowed) {
|
||||||
ctx.isAuthenticated = false
|
finalise(ctx, { authenticated: false })
|
||||||
} else {
|
} else {
|
||||||
ctx.throw(err.status || 403, err)
|
ctx.throw(err.status || 403, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ process.env.MINIO_ACCESS_KEY = "budibase"
|
||||||
process.env.MINIO_SECRET_KEY = "budibase"
|
process.env.MINIO_SECRET_KEY = "budibase"
|
||||||
process.env.COUCH_DB_USER = "budibase"
|
process.env.COUCH_DB_USER = "budibase"
|
||||||
process.env.COUCH_DB_PASSWORD = "budibase"
|
process.env.COUCH_DB_PASSWORD = "budibase"
|
||||||
|
process.env.INTERNAL_API_KEY = "budibase"
|
||||||
|
|
||||||
// Stop info logs polluting test outputs
|
// Stop info logs polluting test outputs
|
||||||
process.env.LOG_LEVEL = "error"
|
process.env.LOG_LEVEL = "error"
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { organisation, auth } from "stores/portal"
|
import { organisation, auth } from "stores/portal"
|
||||||
import Logo from "assets/bb-emblem.svg"
|
import Logo from "assets/bb-emblem.svg"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
let email = ""
|
let email = ""
|
||||||
|
|
||||||
|
@ -20,6 +21,10 @@
|
||||||
notifications.error("Unable to send reset password link")
|
notifications.error("Unable to send reset password link")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await organisation.init()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="login">
|
<div class="login">
|
||||||
|
|
|
@ -10,13 +10,16 @@
|
||||||
notifications,
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { goto, params } from "@roxi/routify"
|
import { goto, params } from "@roxi/routify"
|
||||||
import { auth } from "stores/portal"
|
import { auth, organisation } from "stores/portal"
|
||||||
import GoogleButton from "./_components/GoogleButton.svelte"
|
import GoogleButton from "./_components/GoogleButton.svelte"
|
||||||
import Logo from "assets/bb-emblem.svg"
|
import Logo from "assets/bb-emblem.svg"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
let username = ""
|
let username = ""
|
||||||
let password = ""
|
let password = ""
|
||||||
|
|
||||||
|
$: company = $organisation.company || "Budibase"
|
||||||
|
|
||||||
async function login() {
|
async function login() {
|
||||||
try {
|
try {
|
||||||
await auth.login({
|
await auth.login({
|
||||||
|
@ -43,6 +46,10 @@
|
||||||
function handleKeydown(evt) {
|
function handleKeydown(evt) {
|
||||||
if (evt.key === "Enter") login()
|
if (evt.key === "Enter") login()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await organisation.init()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={handleKeydown} />
|
<svelte:window on:keydown={handleKeydown} />
|
||||||
|
@ -50,8 +57,8 @@
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout noPadding justifyItems="center">
|
<Layout noPadding justifyItems="center">
|
||||||
<img alt="logo" src={Logo} />
|
<img alt="logo" src={$organisation.logoUrl || Logo} />
|
||||||
<Heading>Sign in to Budibase</Heading>
|
<Heading>Sign in to {company}</Heading>
|
||||||
</Layout>
|
</Layout>
|
||||||
<GoogleButton />
|
<GoogleButton />
|
||||||
<Divider noGrid />
|
<Divider noGrid />
|
||||||
|
@ -66,7 +73,7 @@
|
||||||
/>
|
/>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Button cta on:click={login}>Sign in to Budibase</Button>
|
<Button cta on:click={login}>Sign in to {company}</Button>
|
||||||
<ActionButton quiet on:click={() => $goto("./forgot")}>
|
<ActionButton quiet on:click={() => $goto("./forgot")}>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
import { Body, Button, Heading, Layout, notifications } from "@budibase/bbui"
|
import { Body, Button, Heading, Layout, notifications } from "@budibase/bbui"
|
||||||
import { goto, params } from "@roxi/routify"
|
import { goto, params } from "@roxi/routify"
|
||||||
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
|
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
|
||||||
import { auth } from "stores/portal"
|
import { auth, organisation } from "stores/portal"
|
||||||
import Logo from "assets/bb-emblem.svg"
|
import Logo from "assets/bb-emblem.svg"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
const resetCode = $params["?code"]
|
const resetCode = $params["?code"]
|
||||||
let password, error
|
let password, error
|
||||||
|
@ -28,13 +29,17 @@
|
||||||
notifications.error("Unable to reset password")
|
notifications.error("Unable to reset password")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await organisation.init()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout noPadding justifyItems="center">
|
<Layout noPadding justifyItems="center">
|
||||||
<img src={Logo} alt="Organisation logo" />
|
<img src={$organisation.logoUrl || Logo} alt="Organisation logo" />
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading textAlign="center">Reset your password</Heading>
|
<Heading textAlign="center">Reset your password</Heading>
|
||||||
|
|
|
@ -57,11 +57,17 @@
|
||||||
await organisation.init()
|
await organisation.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update settings
|
const config = {
|
||||||
const res = await organisation.save({
|
|
||||||
company: $values.company ?? "",
|
company: $values.company ?? "",
|
||||||
platformUrl: $values.platformUrl ?? "",
|
platformUrl: $values.platformUrl ?? "",
|
||||||
})
|
}
|
||||||
|
// remove logo if required
|
||||||
|
if (!$values.logo) {
|
||||||
|
config.logoUrl = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update settings
|
||||||
|
const res = await organisation.save(config)
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
notifications.success("Settings saved successfully")
|
notifications.success("Settings saved successfully")
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,7 +104,11 @@
|
||||||
<Dropzone
|
<Dropzone
|
||||||
value={[$values.logo]}
|
value={[$values.logo]}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
$values.logo = e.detail?.[0]
|
if (!e.detail || e.detail.length === 0) {
|
||||||
|
$values.logo = null
|
||||||
|
} else {
|
||||||
|
$values.logo = e.detail[0]
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,7 @@ export function createOrganisationStore() {
|
||||||
const { subscribe, set } = store
|
const { subscribe, set } = store
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const res = await api.get(`/api/admin/configs/settings`)
|
const res = await api.get(`/api/admin/configs/public`)
|
||||||
const json = await res.json()
|
const json = await res.json()
|
||||||
|
|
||||||
if (json.status === 400) {
|
if (json.status === 400) {
|
||||||
|
|
|
@ -11,10 +11,14 @@ async function redirect(ctx, method) {
|
||||||
const { devPath } = ctx.params
|
const { devPath } = ctx.params
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
checkSlashesInUrl(`${env.WORKER_URL}/api/admin/${devPath}`),
|
checkSlashesInUrl(`${env.WORKER_URL}/api/admin/${devPath}`),
|
||||||
request(ctx, {
|
request(
|
||||||
|
ctx,
|
||||||
|
{
|
||||||
method,
|
method,
|
||||||
body: ctx.request.body,
|
body: ctx.request.body,
|
||||||
})
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
ctx.throw(response.status, response.statusText)
|
ctx.throw(response.status, response.statusText)
|
||||||
|
|
|
@ -90,7 +90,25 @@ exports.find = async function (ctx) {
|
||||||
if (scopedConfig) {
|
if (scopedConfig) {
|
||||||
ctx.body = scopedConfig
|
ctx.body = scopedConfig
|
||||||
} else {
|
} else {
|
||||||
ctx.throw(400, "No configuration exists.")
|
// don't throw an error, there simply is nothing to return
|
||||||
|
ctx.body = {}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
ctx.throw(err.status, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.publicSettings = async function (ctx) {
|
||||||
|
const db = new CouchDB(GLOBAL_DB)
|
||||||
|
try {
|
||||||
|
// Find the config with the most granular scope based on context
|
||||||
|
const config = await getScopedFullConfig(db, {
|
||||||
|
type: Configs.SETTINGS,
|
||||||
|
})
|
||||||
|
if (!config) {
|
||||||
|
ctx.body = {}
|
||||||
|
} else {
|
||||||
|
ctx.body = config
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(err.status, err)
|
ctx.throw(err.status, err)
|
||||||
|
|
|
@ -130,6 +130,9 @@ exports.removeAppRole = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getSelf = async ctx => {
|
exports.getSelf = async ctx => {
|
||||||
|
if (!ctx.user) {
|
||||||
|
ctx.throw(403, "User not logged in")
|
||||||
|
}
|
||||||
ctx.params = {
|
ctx.params = {
|
||||||
id: ctx.user._id,
|
id: ctx.user._id,
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,10 @@ const PUBLIC_ENDPOINTS = [
|
||||||
route: "/api/apps",
|
route: "/api/apps",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
route: "/api/admin/configs/public",
|
||||||
|
method: "GET",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = new Router()
|
const router = new Router()
|
||||||
|
|
|
@ -26,7 +26,7 @@ function settingValidation() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
platformUrl: Joi.string().optional(),
|
platformUrl: Joi.string().optional(),
|
||||||
logoUrl: Joi.string().optional(),
|
logoUrl: Joi.string().optional().allow("", null),
|
||||||
docsUrl: Joi.string().optional(),
|
docsUrl: Joi.string().optional(),
|
||||||
company: Joi.string().required(),
|
company: Joi.string().required(),
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
|
@ -91,6 +91,7 @@ router
|
||||||
buildConfigGetValidation(),
|
buildConfigGetValidation(),
|
||||||
controller.fetch
|
controller.fetch
|
||||||
)
|
)
|
||||||
|
.get("/api/admin/configs/public", controller.publicSettings)
|
||||||
.get("/api/admin/configs/:type", buildConfigGetValidation(), controller.find)
|
.get("/api/admin/configs/:type", buildConfigGetValidation(), controller.find)
|
||||||
.post(
|
.post(
|
||||||
"/api/admin/configs/upload/:type/:name",
|
"/api/admin/configs/upload/:type/:name",
|
||||||
|
|
Loading…
Reference in New Issue