Merge branch 'develop' of github.com:Budibase/budibase into plugins-dev-experience
This commit is contained in:
commit
f14af4bae9
|
@ -1,4 +1,4 @@
|
||||||
name: Budibase Smoke Test
|
name: Budibase Nightly Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
@ -6,7 +6,7 @@ on:
|
||||||
- cron: "0 5 * * *" # every day at 5AM
|
- cron: "0 5 * * *" # every day at 5AM
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
nightly:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -43,6 +43,18 @@ jobs:
|
||||||
name: Test Reports
|
name: Test Reports
|
||||||
path: packages/builder/cypress/reports/testReport.html
|
path: packages/builder/cypress/reports/testReport.html
|
||||||
|
|
||||||
|
# TODO: enable once running in QA test env
|
||||||
|
# - 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
|
||||||
|
|
||||||
|
# - name: Upload test results HTML
|
||||||
|
# uses: aws-actions/configure-aws-credentials@v1
|
||||||
|
# run: aws s3 cp packages/builder/cypress/reports/testReport.html s3://{{ secrets.BUDI_QA_REPORTS_BUCKET_NAME }}/$GITHUB_RUN_ID/index.html
|
||||||
|
|
||||||
- name: Cypress Discord Notify
|
- name: Cypress Discord Notify
|
||||||
run: yarn test:e2e:ci:notify
|
run: yarn test:e2e:ci:notify
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
import filterTests from "../../support/filterTests"
|
||||||
|
// const interact = require("../support/interact")
|
||||||
|
|
||||||
|
filterTests(["smoke", "all"], () => {
|
||||||
|
context("Auth Configuration", () => {
|
||||||
|
before(() => {
|
||||||
|
cy.login()
|
||||||
|
})
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
cy.get(".spectrum-SideNav li").contains("Auth").click()
|
||||||
|
cy.location().should(loc => {
|
||||||
|
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.get("[data-cy=new-scope-input]").clear()
|
||||||
|
|
||||||
|
cy.get("div.content").scrollTo("bottom")
|
||||||
|
cy.get("[data-cy=oidc-active]").click()
|
||||||
|
|
||||||
|
cy.get("[data-cy=oidc-active]").should('not.be.checked')
|
||||||
|
|
||||||
|
cy.intercept("POST", "/api/global/configs").as("updateAuth")
|
||||||
|
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
|
||||||
|
cy.wait("@updateAuth")
|
||||||
|
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
|
||||||
|
|
||||||
|
cy.get(".spectrum-Toast-content")
|
||||||
|
.contains("Settings saved")
|
||||||
|
.should("be.visible")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should allow updating of the OIDC config", () => {
|
||||||
|
cy.get(".spectrum-SideNav li").contains("Auth").click()
|
||||||
|
cy.location().should(loc => {
|
||||||
|
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
|
||||||
|
})
|
||||||
|
cy.get("div.content").scrollTo("bottom")
|
||||||
|
cy.get(".spectrum-Toast .spectrum-ClearButton").click()
|
||||||
|
|
||||||
|
cy.get("input[data-cy=configUrl]").type("http://budi-auth.com/v2")
|
||||||
|
cy.get("input[data-cy=clientID]").type("34ac6a13-f24a-4b52-c70d-fa544ffd11b2")
|
||||||
|
cy.get("input[data-cy=clientSecret]").type("12A8Q~4nS_DWhOOJ2vWIRsNyDVsdtXPD.Zxa9df_")
|
||||||
|
|
||||||
|
cy.get("button[data-cy=oidc-save]").should("not.be.disabled");
|
||||||
|
|
||||||
|
cy.intercept("POST", "/api/global/configs").as("updateAuth")
|
||||||
|
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
|
||||||
|
cy.wait("@updateAuth")
|
||||||
|
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
|
||||||
|
|
||||||
|
cy.get(".spectrum-Toast-content")
|
||||||
|
.contains("Settings saved")
|
||||||
|
.should("be.visible")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should display default scopes in advanced config.", () => {
|
||||||
|
cy.get(".spectrum-SideNav li").contains("Auth").click()
|
||||||
|
cy.location().should(loc => {
|
||||||
|
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
|
||||||
|
})
|
||||||
|
cy.get("div.content").scrollTo("bottom")
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags-item").contains("openid")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("openid").find(".spectrum-ClearButton").should("not.exist")
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags-item").contains("offline_access")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("email")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("profile")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Add a new scopes", () => {
|
||||||
|
cy.get(".spectrum-SideNav li").contains("Auth").click()
|
||||||
|
cy.location().should(loc => {
|
||||||
|
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
|
||||||
|
})
|
||||||
|
cy.get("div.content").scrollTo("bottom")
|
||||||
|
|
||||||
|
cy.get("[data-cy=new-scope-input]").type("Sample{enter}")
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 5)
|
||||||
|
cy.get(".spectrum-Tags-item").contains("Sample")
|
||||||
|
|
||||||
|
cy.get(".auth-form input.spectrum-Textfield-input").type("Another ")
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 6)
|
||||||
|
cy.get(".spectrum-Tags-item").contains("Another")
|
||||||
|
|
||||||
|
cy.get("button[data-cy=oidc-save]").should("not.be.disabled");
|
||||||
|
|
||||||
|
cy.intercept("POST", "/api/global/configs").as("updateAuth")
|
||||||
|
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
|
||||||
|
cy.wait("@updateAuth")
|
||||||
|
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
|
||||||
|
|
||||||
|
cy.reload()
|
||||||
|
|
||||||
|
cy.get("div.content").scrollTo("bottom")
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags-item").contains("openid")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("offline_access")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("email")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("profile")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("Sample")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("Another")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should allow the removal of auth scopes", () => {
|
||||||
|
cy.get(".spectrum-SideNav li").contains("Auth").click()
|
||||||
|
cy.location().should(loc => {
|
||||||
|
expect(loc.pathname).to.eq("/builder/portal/manage/auth")
|
||||||
|
})
|
||||||
|
cy.get("div.content").scrollTo("bottom")
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags-item").contains("offline_access").parent().find(".spectrum-ClearButton").click()
|
||||||
|
cy.get(".spectrum-Tags-item").contains("profile").parent().find(".spectrum-ClearButton").click()
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags-item").contains("offline_access").should("not.exist")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("profile").should("not.exist")
|
||||||
|
|
||||||
|
cy.get("button[data-cy=oidc-save]").should("not.be.disabled");
|
||||||
|
|
||||||
|
cy.intercept("POST", "/api/global/configs").as("updateAuth")
|
||||||
|
cy.get("button[data-cy=oidc-save]").contains("Save").click({force: true})
|
||||||
|
cy.wait("@updateAuth")
|
||||||
|
cy.get("@updateAuth").its("response.statusCode").should("eq", 200)
|
||||||
|
|
||||||
|
cy.get(".spectrum-Toast-content")
|
||||||
|
.contains("Settings saved")
|
||||||
|
.should("be.visible")
|
||||||
|
|
||||||
|
cy.reload()
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags-item").contains("offline_access").should("not.exist")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("profile").should("not.exist")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should allow auth scopes to be reset to the core defaults.", () => {
|
||||||
|
cy.get(".spectrum-SideNav li").contains("Auth").click()
|
||||||
|
|
||||||
|
cy.get("div.content").scrollTo("bottom")
|
||||||
|
|
||||||
|
cy.get("[data-cy=restore-oidc-default-scopes]").click({force: true})
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
|
||||||
|
|
||||||
|
cy.get(".spectrum-Tags-item").contains("openid")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("offline_access")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("email")
|
||||||
|
cy.get(".spectrum-Tags-item").contains("profile")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should not allow invalid characters in the auth scopes", () => {
|
||||||
|
cy.get("[data-cy=new-scope-input]").type("thisIsInvalid\\{enter}")
|
||||||
|
cy.get(".spectrum-Form-itemField .error").contains("Auth scopes cannot contain spaces, double quotes or backslashes")
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
|
||||||
|
|
||||||
|
cy.get("[data-cy=new-scope-input]").clear()
|
||||||
|
|
||||||
|
cy.get("[data-cy=new-scope-input]").type("alsoInvalid\"{enter}")
|
||||||
|
cy.get(".spectrum-Form-itemField .error").contains("Auth scopes cannot contain spaces, double quotes or backslashes")
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
|
||||||
|
|
||||||
|
cy.get("[data-cy=new-scope-input]").clear()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should not allow duplicate auth scopes", () => {
|
||||||
|
cy.get("[data-cy=new-scope-input]").type("offline_access{enter}")
|
||||||
|
cy.get(".spectrum-Form-itemField .error").contains("Auth scope already exists")
|
||||||
|
cy.get(".spectrum-Tags").find(".spectrum-Tags-item").its("length").should("eq", 4)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
|
@ -17,7 +17,7 @@
|
||||||
"cy:run:ci:record": "xvfb-run cypress run --headed --browser chrome --record",
|
"cy:run:ci:record": "xvfb-run cypress run --headed --browser chrome --record",
|
||||||
"cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run",
|
"cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run",
|
||||||
"cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci",
|
"cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci",
|
||||||
"cy:ci:record": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci:record && npm run cy:ci:report",
|
"cy:ci:record": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci:record; npm run cy:ci:report",
|
||||||
"cy:ci:report": "mochawesome-merge cypress/reports/*.json > cypress/reports/testReport.json && marge cypress/reports/testReport.json --reportDir cypress/reports --inline",
|
"cy:ci:report": "mochawesome-merge cypress/reports/*.json > cypress/reports/testReport.json && marge cypress/reports/testReport.json --reportDir cypress/reports --inline",
|
||||||
"cy:ci:notify": "node scripts/cypressResultsWebhook",
|
"cy:ci:notify": "node scripts/cypressResultsWebhook",
|
||||||
"cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open",
|
"cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open",
|
||||||
|
|
|
@ -5,7 +5,6 @@ const path = require("path")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
|
|
||||||
const WEBHOOK_URL = process.env.CYPRESS_WEBHOOK_URL
|
const WEBHOOK_URL = process.env.CYPRESS_WEBHOOK_URL
|
||||||
const OUTCOME = process.env.CYPRESS_OUTCOME
|
|
||||||
const DASHBOARD_URL = process.env.CYPRESS_DASHBOARD_URL
|
const DASHBOARD_URL = process.env.CYPRESS_DASHBOARD_URL
|
||||||
const GIT_SHA = process.env.GITHUB_SHA
|
const GIT_SHA = process.env.GITHUB_SHA
|
||||||
const GITHUB_ACTIONS_RUN_URL = process.env.GITHUB_ACTIONS_RUN_URL
|
const GITHUB_ACTIONS_RUN_URL = process.env.GITHUB_ACTIONS_RUN_URL
|
||||||
|
@ -35,6 +34,8 @@ async function discordCypressResultsNotification(report) {
|
||||||
skipped,
|
skipped,
|
||||||
} = report.stats
|
} = report.stats
|
||||||
|
|
||||||
|
const OUTCOME = failures > 0 ? "failure" : "success"
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -114,7 +115,7 @@ async function discordCypressResultsNotification(report) {
|
||||||
}
|
}
|
||||||
const response = await fetch(WEBHOOK_URL, options)
|
const response = await fetch(WEBHOOK_URL, options)
|
||||||
|
|
||||||
if (response.status >= 400) {
|
if (response.status >= 201) {
|
||||||
const text = await response.text()
|
const text = await response.text()
|
||||||
console.error(
|
console.error(
|
||||||
`Error sending discord webhook. \nStatus: ${response.status}. \nResponse Body: ${text}. \nRequest Body: ${options.body}`
|
`Error sending discord webhook. \nStatus: ${response.status}. \nResponse Body: ${text}. \nRequest Body: ${options.body}`
|
||||||
|
|
|
@ -40,6 +40,7 @@ const INITIAL_FRONTEND_STATE = {
|
||||||
devicePreview: false,
|
devicePreview: false,
|
||||||
messagePassing: false,
|
messagePassing: false,
|
||||||
continueIfAction: false,
|
continueIfAction: false,
|
||||||
|
showNotificationAction: false,
|
||||||
},
|
},
|
||||||
errors: [],
|
errors: [],
|
||||||
hasAppPackage: false,
|
hasAppPackage: false,
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<script>
|
||||||
|
import { Select, Label, Checkbox } from "@budibase/bbui"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||||
|
|
||||||
|
export let parameters
|
||||||
|
export let bindings = []
|
||||||
|
|
||||||
|
const types = [
|
||||||
|
{
|
||||||
|
label: "Success",
|
||||||
|
value: "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Warning",
|
||||||
|
value: "warning",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Error",
|
||||||
|
value: "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Info",
|
||||||
|
value: "info",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!parameters.type) {
|
||||||
|
parameters.type = "success"
|
||||||
|
}
|
||||||
|
if (parameters.autoDismiss == null) {
|
||||||
|
parameters.autoDismiss = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="root">
|
||||||
|
<Label>Type</Label>
|
||||||
|
<Select bind:value={parameters.type} options={types} placeholder={null} />
|
||||||
|
<Label>Message</Label>
|
||||||
|
<DrawerBindableInput
|
||||||
|
{bindings}
|
||||||
|
value={parameters.message}
|
||||||
|
on:change={e => (parameters.message = e.detail)}
|
||||||
|
/>
|
||||||
|
<Label />
|
||||||
|
<Checkbox text="Auto dismiss" bind:value={parameters.autoDismiss} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.root {
|
||||||
|
display: grid;
|
||||||
|
column-gap: var(--spacing-l);
|
||||||
|
row-gap: var(--spacing-s);
|
||||||
|
grid-template-columns: 60px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -15,3 +15,4 @@ export { default as S3Upload } from "./S3Upload.svelte"
|
||||||
export { default as ExportData } from "./ExportData.svelte"
|
export { default as ExportData } from "./ExportData.svelte"
|
||||||
export { default as ContinueIf } from "./ContinueIf.svelte"
|
export { default as ContinueIf } from "./ContinueIf.svelte"
|
||||||
export { default as UpdateFieldValue } from "./UpdateFieldValue.svelte"
|
export { default as UpdateFieldValue } from "./UpdateFieldValue.svelte"
|
||||||
|
export { default as ShowNotification } from "./ShowNotification.svelte"
|
||||||
|
|
|
@ -110,6 +110,12 @@
|
||||||
"type": "logic",
|
"type": "logic",
|
||||||
"component": "ContinueIf",
|
"component": "ContinueIf",
|
||||||
"dependsOnFeature": "continueIfAction"
|
"dependsOnFeature": "continueIfAction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Show Notification",
|
||||||
|
"type": "application",
|
||||||
|
"component": "ShowNotification",
|
||||||
|
"dependsOnFeature": "showNotificationAction"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -18,6 +18,8 @@
|
||||||
Body,
|
Body,
|
||||||
Select,
|
Select,
|
||||||
Toggle,
|
Toggle,
|
||||||
|
Tag,
|
||||||
|
Tags,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
@ -29,6 +31,8 @@
|
||||||
OIDC: "oidc",
|
OIDC: "oidc",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HasSpacesRegex = /[\\"\s]/
|
||||||
|
|
||||||
// Some older google configs contain a manually specified value - retain the functionality to edit the field
|
// Some older google configs contain a manually specified value - retain the functionality to edit the field
|
||||||
// When there is no value or we are in the cloud - prohibit editing the field, must use platform url to change
|
// When there is no value or we are in the cloud - prohibit editing the field, must use platform url to change
|
||||||
$: googleCallbackUrl = undefined
|
$: googleCallbackUrl = undefined
|
||||||
|
@ -145,7 +149,6 @@
|
||||||
|
|
||||||
async function save(docs) {
|
async function save(docs) {
|
||||||
let calls = []
|
let calls = []
|
||||||
|
|
||||||
// Only if the user has provided an image, upload it
|
// Only if the user has provided an image, upload it
|
||||||
if (image) {
|
if (image) {
|
||||||
let data = new FormData()
|
let data = new FormData()
|
||||||
|
@ -157,7 +160,6 @@
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
docs.forEach(element => {
|
docs.forEach(element => {
|
||||||
// Delete unsupported fields
|
// Delete unsupported fields
|
||||||
delete element.createdAt
|
delete element.createdAt
|
||||||
|
@ -199,7 +201,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (calls.length) {
|
if (calls.length) {
|
||||||
Promise.all(calls)
|
Promise.all(calls)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
@ -215,6 +216,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let defaultScopes = ["profile", "email", "offline_access"]
|
||||||
|
|
||||||
|
const refreshScopes = idx => {
|
||||||
|
providers.oidc.config.configs[idx]["scopes"] =
|
||||||
|
providers.oidc.config.configs[idx]["scopes"]
|
||||||
|
}
|
||||||
|
|
||||||
|
let scopesFields = [
|
||||||
|
{
|
||||||
|
editing: true,
|
||||||
|
inputText: null,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
await organisation.init()
|
await organisation.init()
|
||||||
|
@ -276,7 +292,7 @@
|
||||||
if (!oidcDoc?._id) {
|
if (!oidcDoc?._id) {
|
||||||
providers.oidc = {
|
providers.oidc = {
|
||||||
type: ConfigTypes.OIDC,
|
type: ConfigTypes.OIDC,
|
||||||
config: { configs: [{ activated: true }] },
|
config: { configs: [{ activated: true, scopes: defaultScopes }] },
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
originalOidcDoc = cloneDeep(oidcDoc)
|
originalOidcDoc = cloneDeep(oidcDoc)
|
||||||
|
@ -345,6 +361,7 @@
|
||||||
size="s"
|
size="s"
|
||||||
cta
|
cta
|
||||||
on:click={() => save([providers.oidc])}
|
on:click={() => save([providers.oidc])}
|
||||||
|
dataCy={"oidc-save"}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -362,6 +379,7 @@
|
||||||
bind:value={providers.oidc.config.configs[0][field.name]}
|
bind:value={providers.oidc.config.configs[0][field.name]}
|
||||||
readonly={field.readonly}
|
readonly={field.readonly}
|
||||||
placeholder={field.placeholder}
|
placeholder={field.placeholder}
|
||||||
|
dataCy={field.name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -392,15 +410,132 @@
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<Label size="L">Activated</Label>
|
<Label size="L">Activated</Label>
|
||||||
<Toggle
|
<Toggle
|
||||||
|
dataCy={"oidc-active"}
|
||||||
text=""
|
text=""
|
||||||
bind:value={providers.oidc.config.configs[0].activated}
|
bind:value={providers.oidc.config.configs[0].activated}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
<span class="advanced-config">
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
<Heading size="XS">
|
||||||
|
<div class="auth-scopes">
|
||||||
|
<div>Advanced</div>
|
||||||
|
<Button
|
||||||
|
secondary
|
||||||
|
newStyles
|
||||||
|
size="S"
|
||||||
|
on:click={() => {
|
||||||
|
providers.oidc.config.configs[0]["scopes"] = [...defaultScopes]
|
||||||
|
}}
|
||||||
|
dataCy={"restore-oidc-default-scopes"}
|
||||||
|
>
|
||||||
|
Restore Defaults
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Heading>
|
||||||
|
<Body size="S">
|
||||||
|
Changes to your authentication scopes will only take effect when you
|
||||||
|
next log in. Please refer to your vendor documentation before
|
||||||
|
modification.
|
||||||
|
</Body>
|
||||||
|
|
||||||
|
<div class="auth-form">
|
||||||
|
<span class="add-new">
|
||||||
|
<Label size="L">{"Auth Scopes"}</Label>
|
||||||
|
<Input
|
||||||
|
dataCy={"new-scope-input"}
|
||||||
|
error={scopesFields[0].error}
|
||||||
|
placeholder={"New Scope"}
|
||||||
|
bind:value={scopesFields[0].inputText}
|
||||||
|
on:keyup={e => {
|
||||||
|
if (!scopesFields[0].inputText) {
|
||||||
|
scopesFields[0].error = null
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
e.key === "Enter" ||
|
||||||
|
e.keyCode === 13 ||
|
||||||
|
e.code == "Space" ||
|
||||||
|
e.keyCode == 32
|
||||||
|
) {
|
||||||
|
let scopes = providers.oidc.config.configs[0]["scopes"]
|
||||||
|
? providers.oidc.config.configs[0]["scopes"]
|
||||||
|
: [...defaultScopes]
|
||||||
|
|
||||||
|
let update = scopesFields[0].inputText.trim()
|
||||||
|
|
||||||
|
if (HasSpacesRegex.test(update)) {
|
||||||
|
scopesFields[0].error =
|
||||||
|
"Auth scopes cannot contain spaces, double quotes or backslashes"
|
||||||
|
return
|
||||||
|
} else if (scopes.indexOf(update) > -1) {
|
||||||
|
scopesFields[0].error = "Auth scope already exists"
|
||||||
|
return
|
||||||
|
} else if (!update.length) {
|
||||||
|
scopesFields[0].inputText = null
|
||||||
|
scopesFields[0].error = null
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
scopesFields[0].error = null
|
||||||
|
scopes.push(update)
|
||||||
|
providers.oidc.config.configs[0]["scopes"] = scopes
|
||||||
|
scopesFields[0].inputText = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div class="tag-wrap">
|
||||||
|
<span />
|
||||||
|
<Tags>
|
||||||
|
<Tag closable={false}>openid</Tag>
|
||||||
|
{#each providers.oidc.config.configs[0]["scopes"] || [...defaultScopes] as tag, idx}
|
||||||
|
<Tag
|
||||||
|
closable={scopesFields[0].editing}
|
||||||
|
on:click={() => {
|
||||||
|
let idxScopes = providers.oidc.config.configs[0]["scopes"]
|
||||||
|
if (idxScopes.length == 1) {
|
||||||
|
idxScopes.pop()
|
||||||
|
} else {
|
||||||
|
idxScopes.splice(idx, 1)
|
||||||
|
refreshScopes(0)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
{/each}
|
||||||
|
</Tags>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.auth-scopes {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-config :global(.spectrum-Tags-item) {
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-top: var(--spacing-m);
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form > * {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
grid-template-columns: 100px 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-config .auth-form .tag-wrap {
|
||||||
|
padding: 0px 5px 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 100px 1fr;
|
grid-template-columns: 100px 1fr;
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"devicePreview": true,
|
"devicePreview": true,
|
||||||
"messagePassing": true,
|
"messagePassing": true,
|
||||||
"rowSelection": true,
|
"rowSelection": true,
|
||||||
"continueIfAction": true
|
"continueIfAction": true,
|
||||||
|
"showNotificationAction": true
|
||||||
},
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
"name": "Layout",
|
"name": "Layout",
|
||||||
|
|
|
@ -62,10 +62,14 @@ const createNotificationStore = () => {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
actions: {
|
actions: {
|
||||||
send,
|
send,
|
||||||
info: msg => send(msg, "info", "Info"),
|
info: (msg, autoDismiss) =>
|
||||||
success: msg => send(msg, "success", "CheckmarkCircle"),
|
send(msg, "info", "Info", autoDismiss ?? true),
|
||||||
warning: msg => send(msg, "warning", "Alert"),
|
success: (msg, autoDismiss) =>
|
||||||
error: msg => send(msg, "error", "Alert", false),
|
send(msg, "success", "CheckmarkCircle", autoDismiss ?? true),
|
||||||
|
warning: (msg, autoDismiss) =>
|
||||||
|
send(msg, "warning", "Alert", autoDismiss ?? true),
|
||||||
|
error: (msg, autoDismiss) =>
|
||||||
|
send(msg, "error", "Alert", autoDismiss ?? false),
|
||||||
blockNotifications,
|
blockNotifications,
|
||||||
dismiss,
|
dismiss,
|
||||||
},
|
},
|
||||||
|
|
|
@ -300,6 +300,14 @@ const continueIfHandler = action => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showNotificationHandler = action => {
|
||||||
|
const { message, type, autoDismiss } = action.parameters
|
||||||
|
if (!message || !type) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
notificationStore.actions[type]?.(message, autoDismiss)
|
||||||
|
}
|
||||||
|
|
||||||
const handlerMap = {
|
const handlerMap = {
|
||||||
["Save Row"]: saveRowHandler,
|
["Save Row"]: saveRowHandler,
|
||||||
["Duplicate Row"]: duplicateRowHandler,
|
["Duplicate Row"]: duplicateRowHandler,
|
||||||
|
@ -318,6 +326,7 @@ const handlerMap = {
|
||||||
["Upload File to S3"]: s3UploadHandler,
|
["Upload File to S3"]: s3UploadHandler,
|
||||||
["Export Data"]: exportDataHandler,
|
["Export Data"]: exportDataHandler,
|
||||||
["Continue if / Stop if"]: continueIfHandler,
|
["Continue if / Stop if"]: continueIfHandler,
|
||||||
|
["Show Notification"]: showNotificationHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmTextMap = {
|
const confirmTextMap = {
|
||||||
|
|
|
@ -227,9 +227,22 @@ export const oidcPreAuth = async (ctx: any, next: any) => {
|
||||||
|
|
||||||
setCookie(ctx, configId, Cookies.OIDC_CONFIG)
|
setCookie(ctx, configId, Cookies.OIDC_CONFIG)
|
||||||
|
|
||||||
|
const db = getGlobalDB()
|
||||||
|
const config = await core.db.getScopedConfig(db, {
|
||||||
|
type: Configs.OIDC,
|
||||||
|
group: ctx.query.group,
|
||||||
|
})
|
||||||
|
|
||||||
|
const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
|
||||||
|
|
||||||
|
let authScopes =
|
||||||
|
chosenConfig.scopes?.length > 0
|
||||||
|
? chosenConfig.scopes
|
||||||
|
: ["profile", "email", "offline_access"]
|
||||||
|
|
||||||
return passport.authenticate(strategy, {
|
return passport.authenticate(strategy, {
|
||||||
// required 'openid' scope is added by oidc strategy factory
|
// required 'openid' scope is added by oidc strategy factory
|
||||||
scope: ["profile", "email", "offline_access"], //auth0 offline_access scope required for the refresh token behaviour.
|
scope: authScopes,
|
||||||
})(ctx, next)
|
})(ctx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ function oidcValidation() {
|
||||||
name: Joi.string().allow("", null),
|
name: Joi.string().allow("", null),
|
||||||
uuid: Joi.string().required(),
|
uuid: Joi.string().required(),
|
||||||
activated: Joi.boolean().required(),
|
activated: Joi.boolean().required(),
|
||||||
|
scopes: Joi.array().optional()
|
||||||
})
|
})
|
||||||
).required(true)
|
).required(true)
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
|
|
Loading…
Reference in New Issue