diff --git a/.github/workflows/smoke_test.yaml b/.github/workflows/smoke_test.yaml new file mode 100644 index 0000000000..745fed1306 --- /dev/null +++ b/.github/workflows/smoke_test.yaml @@ -0,0 +1,46 @@ +name: Budibase Smoke Test + +on: + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 14.x + uses: actions/setup-node@v1 + with: + node-version: 14.x + - run: yarn + - run: yarn bootstrap + - run: yarn build + - name: Pull cypress.env.yaml from budibase-infra + run: | + curl -H "Authorization: token ${{ secrets.GH_PERSONAL_TOKEN }}" \ + -H 'Accept: application/vnd.github.v3.raw' \ + -o packages/builder/cypress.env.json \ + -L https://api.github.com/repos/budibase/budibase-infra/contents/test/cypress.env.json + wc -l packages/builder/cypress.env.json + - run: yarn test:e2e:ci + env: + CI: true + name: Budibase CI + + # TODO: upload recordings to s3 + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v1 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: eu-west-1 + + # TODO look at cypress reporters + # - name: Discord Webhook Action + # uses: tsickert/discord-webhook@v4.0.0 + # with: + # webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }} + # content: "Production Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Cloud." + # embed-title: ${{ env.RELEASE_VERSION }} + diff --git a/lerna.json b/lerna.json index 4bbdf26b2f..0ae46b52e4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 648a493712..71d25bc2db 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "description": "Budibase backend core libraries used in server and worker", "main": "src/index.js", "author": "Budibase", diff --git a/packages/backend-core/src/constants.js b/packages/backend-core/src/constants.js index 28b9ced49b..8e6b01608e 100644 --- a/packages/backend-core/src/constants.js +++ b/packages/backend-core/src/constants.js @@ -8,6 +8,7 @@ exports.Cookies = { Auth: "budibase:auth", Init: "budibase:init", OIDC_CONFIG: "budibase:oidc:config", + RETURN_URL: "budibase:returnurl", } exports.Headers = { diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js index 8c00f2a8b8..85dd32946f 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.js @@ -96,7 +96,12 @@ exports.getCookie = (ctx, name) => { * @param {string|object} value The value of cookie which will be set. * @param {object} opts options like whether to sign. */ -exports.setCookie = (ctx, value, name = "builder", opts = { sign: true }) => { +exports.setCookie = ( + ctx, + value, + name = "builder", + opts = { sign: true, requestDomain: false } +) => { if (value && opts && opts.sign) { value = jwt.sign(value, options.secretOrKey) } @@ -108,7 +113,7 @@ exports.setCookie = (ctx, value, name = "builder", opts = { sign: true }) => { overwrite: true, } - if (environment.COOKIE_DOMAIN) { + if (environment.COOKIE_DOMAIN && !opts.requestDomain) { config.domain = environment.COOKIE_DOMAIN } diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 199ccd6db2..50e23c7b2a 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 06583ffde8..a98180316f 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "license": "GPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^1.0.27-alpha.17", - "@budibase/client": "^1.0.27-alpha.17", + "@budibase/bbui": "^1.0.27-alpha.20", + "@budibase/client": "^1.0.27-alpha.20", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^1.0.27-alpha.17", + "@budibase/string-templates": "^1.0.27-alpha.20", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/builderStore/cookies.js b/packages/builder/src/builderStore/cookies.js index a84f1a4f20..cb4e46ec86 100644 --- a/packages/builder/src/builderStore/cookies.js +++ b/packages/builder/src/builderStore/cookies.js @@ -1,16 +1,26 @@ export const Cookies = { Auth: "budibase:auth", CurrentApp: "budibase:currentapp", + ReturnUrl: "budibase:returnurl", +} + +export function setCookie(name, value) { + if (getCookie(name)) { + removeCookie(name) + } + window.document.cookie = `${name}=${value}; Path=/;` } export function getCookie(cookieName) { - return document.cookie.split(";").some(cookie => { - return cookie.trim().startsWith(`${cookieName}=`) - }) + const value = `; ${document.cookie}` + const parts = value.split(`; ${cookieName}=`) + if (parts.length === 2) { + return parts[1].split(";").shift() + } } export function removeCookie(cookieName) { if (getCookie(cookieName)) { - document.cookie = `${cookieName}=; Max-Age=-99999999;` + document.cookie = `${cookieName}=; Max-Age=-99999999; Path=/;` } } diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte index bf55be5534..12a544096a 100644 --- a/packages/builder/src/pages/builder/_layout.svelte +++ b/packages/builder/src/pages/builder/_layout.svelte @@ -2,6 +2,12 @@ import { isActive, redirect, params } from "@roxi/routify" import { admin, auth } from "stores/portal" import { onMount } from "svelte" + import { + Cookies, + getCookie, + removeCookie, + setCookie, + } from "builderStore/cookies" let loaded = false @@ -67,6 +73,24 @@ $: { const apiReady = $admin.loaded && $auth.loaded + + // firstly, set the return url + if ( + loaded && + apiReady && + !$auth.user && + !getCookie(Cookies.ReturnUrl) && + // logout triggers a page refresh, so we don't want to set the return url + !$auth.postLogout && + // don't set the return url on pre-login pages + !$isActive("./auth") && + !$isActive("./invite") && + !$isActive("./admin") + ) { + const url = window.location.pathname + setCookie(Cookies.ReturnUrl, url) + } + // if tenant is not set go to it if ( loaded && @@ -90,13 +114,20 @@ !$isActive("./invite") && !$isActive("./admin") ) { - const returnUrl = encodeURIComponent(window.location.pathname) - $redirect("./auth?", { returnUrl }) + $redirect("./auth") } // check if password reset required for user else if ($auth.user?.forceResetPassword) { $redirect("./auth/reset") } + // lastly, redirect to the return url if it has been set + else if (loaded && apiReady && $auth.user) { + const returnUrl = getCookie(Cookies.ReturnUrl) + if (returnUrl) { + removeCookie(Cookies.ReturnUrl) + window.location.href = returnUrl + } + } } diff --git a/packages/builder/src/pages/builder/auth/login.svelte b/packages/builder/src/pages/builder/auth/login.svelte index 5a5a27eb6e..7a13164c51 100644 --- a/packages/builder/src/pages/builder/auth/login.svelte +++ b/packages/builder/src/pages/builder/auth/login.svelte @@ -10,7 +10,7 @@ notifications, Link, } from "@budibase/bbui" - import { goto, params } from "@roxi/routify" + import { goto } from "@roxi/routify" import { auth, organisation, oidc, admin } from "stores/portal" import GoogleButton from "./_components/GoogleButton.svelte" import OIDCButton from "./_components/OIDCButton.svelte" @@ -35,12 +35,8 @@ if ($auth?.user?.forceResetPassword) { $goto("./reset") } else { - if ($params["?returnUrl"]) { - window.location = decodeURIComponent($params["?returnUrl"]) - } else { - notifications.success("Logged in successfully") - $goto("../portal") - } + notifications.success("Logged in successfully") + $goto("../portal") } } catch (err) { console.error(err) diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index 6be2c7decf..bdd4d95915 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -9,6 +9,7 @@ export function createAuthStore() { tenantId: "default", tenantSet: false, loaded: false, + postLogout: false, }) const store = derived(auth, $store => { let initials = null @@ -34,6 +35,7 @@ export function createAuthStore() { tenantId: $store.tenantId, tenantSet: $store.tenantSet, loaded: $store.loaded, + postLogout: $store.postLogout, initials, isAdmin, isBuilder, @@ -89,6 +91,13 @@ export function createAuthStore() { return info } + async function setPostLogout() { + auth.update(store => { + store.postLogout = true + return store + }) + } + async function getInitInfo() { const response = await api.get(`/api/global/auth/init`) const json = response.json() @@ -145,6 +154,7 @@ export function createAuthStore() { await response.json() await setInitInfo({}) setUser(null) + setPostLogout() }, updateSelf: async fields => { const newUser = { ...get(auth).user, ...fields } diff --git a/packages/cli/package.json b/packages/cli/package.json index 2f012e7630..a5a8a2025b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 83b742d7fb..c8a1d646bd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^1.0.27-alpha.17", + "@budibase/bbui": "^1.0.27-alpha.20", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^1.0.27-alpha.17", + "@budibase/string-templates": "^1.0.27-alpha.20", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 98dec9667b..7f5bed210e 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -63,8 +63,9 @@ } else { // The user is not logged in, redirect them to login const returnUrl = `${window.location.pathname}${window.location.hash}` - const encodedUrl = encodeURIComponent(returnUrl) - window.location = `/builder/auth/login?returnUrl=${encodedUrl}` + // TODO: reuse `Cookies` from builder when frontend-core is added + window.document.cookie = `budibase:returnurl=${returnUrl}; Path=/` + window.location = `/builder/auth/login` } } } diff --git a/packages/server/package.json b/packages/server/package.json index 4fd5e89f4f..85a29d802e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -70,9 +70,9 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "^10.0.3", - "@budibase/backend-core": "^1.0.27-alpha.17", - "@budibase/client": "^1.0.27-alpha.17", - "@budibase/string-templates": "^1.0.27-alpha.17", + "@budibase/backend-core": "^1.0.27-alpha.20", + "@budibase/client": "^1.0.27-alpha.20", + "@budibase/string-templates": "^1.0.27-alpha.20", "@bull-board/api": "^3.7.0", "@bull-board/koa": "^3.7.0", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index 69f80c895b..e11aefdf1c 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -47,6 +47,15 @@ module.exports = async (ctx, next) => { (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) ) { clearCookie(ctx, Cookies.CurrentApp) + // have to set the return url on the server side as client side is not available + setCookie(ctx, ctx.url, Cookies.RETURN_URL, { + // don't sign so the browser can easily read + sign: false, + // use the request domain to match how ui handles the return url cookie. + // it's important we don't use the shared domain here as the builder + // can't delete from it without awareness of the domain. + requestDomain: true, + }) return ctx.redirect("/") } diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index bbfa64d01a..c6f64f5395 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index d6bb45f9c4..db6391c09e 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "1.0.27-alpha.17", + "version": "1.0.27-alpha.20", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "^1.0.27-alpha.17", - "@budibase/string-templates": "^1.0.27-alpha.17", + "@budibase/backend-core": "^1.0.27-alpha.20", + "@budibase/string-templates": "^1.0.27-alpha.20", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0",