SSL support for digitalocean, started utility function for BB logout, bunch of minor bug fixes

This commit is contained in:
Martin McKeaveney 2021-10-12 16:13:54 +01:00
parent 6da4e25f0a
commit ec26d5c738
15 changed files with 131 additions and 14 deletions

View File

@ -0,0 +1,10 @@
{
"folders": [
{
"path": "../.."
},
{
"path": "../../../budibase-infra"
}
]
}

View File

@ -42,8 +42,9 @@ module.exports = (
internal = false
if (authCookie) {
let error = null
const sessionId = authCookie.sessionId,
userId = authCookie.userId
const sessionId = authCookie.sessionId
const userId = authCookie.userId
const session = await getSession(userId, sessionId)
if (!session) {
error = "No session found"

View File

@ -24,17 +24,24 @@ exports.createASession = async (userId, session) => {
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS)
}
exports.invalidateSessions = async (userId, sessionId = null) => {
exports.invalidateSessions = async (userId, sessionIds = null) => {
let sessions = []
if (sessionId) {
sessions.push({ key: makeSessionID(userId, sessionId) })
} else {
// If no sessionIds, get all the sessions for the user
if (!sessionIds) {
sessions = await getSessionsForUser(userId)
sessions.forEach(
session =>
(session.key = makeSessionID(session.userId, session.sessionId))
)
} else {
// use the passed array of sessionIds
sessions = Array.isArray(sessionIds) ? sessionIds : [sessionIds]
sessions = sessions.map(sessionId => ({
key: makeSessionID(userId, sessionId),
}))
}
const client = await redis.getSessionClient()
const promises = []
for (let session of sessions) {

View File

@ -7,7 +7,7 @@ const {
const jwt = require("jsonwebtoken")
const { options } = require("./middleware/passport/jwt")
const { createUserEmailView } = require("./db/views")
const { Headers, UserStatus } = require("./constants")
const { Headers, UserStatus, Cookies } = require("./constants")
const {
getGlobalDB,
updateTenantId,
@ -19,6 +19,10 @@ const accounts = require("./cloud/accounts")
const { hash } = require("./hashing")
const userCache = require("./cache/user")
const env = require("./environment")
const {
getSessionsForUser,
invalidateSessions,
} = require("./security/sessions")
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
@ -235,3 +239,23 @@ exports.saveUser = async (
}
}
}
/**
* Logs a user out from budibase. Re-used across account portal and builder.
*/
exports.logout = async ({ ctx, userId, sessionId, keepActiveSession }) => {
let sessions = await getSessionsForUser(userId)
if (keepActiveSession) {
sessions = sessions.filter(session => session.sessionId !== sessionId)
}
await invalidateSessions(
userId,
sessions.map(({ sessionId }) => sessionId)
)
// clear cookies
this.clearCookie(ctx, Cookies.Auth)
this.clearCookie(ctx, Cookies.CurrentApp)
}

View File

@ -5,7 +5,7 @@
// ***********************************************
//
Cypress.on('uncaught:exception', (err, runnable) => {
Cypress.on("uncaught:exception", () => {
return false
})

View File

@ -32,6 +32,7 @@
const FORMULA_TYPE = FIELDS.FORMULA.type
const LINK_TYPE = FIELDS.LINK.type
const dispatch = createEventDispatcher()
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev"]
const { hide } = getContext(Context.Modal)
let fieldDefinitions = cloneDeep(FIELDS)
@ -66,7 +67,11 @@
(field.type === LINK_TYPE && !field.tableId) ||
Object.keys($tables.draft?.schema ?? {}).some(
key => key !== originalName && key === field.name
)
) ||
columnNameInvalid
$: columnNameInvalid = PROHIBITED_COLUMN_NAMES.some(
name => field.name === name
)
// used to select what different options can be displayed for column type
$: canBeSearched =
@ -200,6 +205,9 @@
label="Name"
bind:value={field.name}
disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)}
error={columnNameInvalid
? "type, _id and _rev are disallowed as column names"
: ""}
/>
<Select

View File

@ -1,10 +1,18 @@
<script>
import { Label, Input, Layout, Toggle, Button } from "@budibase/bbui"
import {
Label,
Input,
Layout,
Toggle,
Button,
TextArea,
} from "@budibase/bbui"
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
import { capitalise } from "helpers"
export let integration
export let schema
let addButton
</script>
@ -29,6 +37,15 @@
<Label>{capitalise(configKey)}</Label>
<Toggle text="" bind:value={integration[configKey]} />
</div>
{:else if schema[configKey].type === "longForm"}
<div class="form-row">
<Label>{capitalise(configKey)}</Label>
<TextArea
type={schema[configKey].type}
on:change
bind:value={integration[configKey]}
/>
</div>
{:else}
<div class="form-row">
<Label>{capitalise(configKey)}</Label>

View File

@ -60,7 +60,7 @@
</Modal>
<Modal bind:this={externalDatasourceModal}>
<DatasourceConfigModal {integration} />
<DatasourceConfigModal {integration} {modal} />
</Modal>
<Modal bind:this={modal}>

View File

@ -64,7 +64,7 @@
? "Fetch tables from database"
: "Save and continue to query"}
cancelText="Back"
size="M"
size="L"
>
<Layout noPadding>
<Body size="XS"

View File

@ -77,7 +77,7 @@ exports.run = async function ({ inputs }) {
const { status, message } = await getFetchResponse(response)
return {
httpStatus: status,
success: status === 200,
success: status === 200 || status === 204,
response: message,
}
}

View File

@ -20,12 +20,14 @@ export enum QueryTypes {
export enum DatasourceFieldTypes {
STRING = "string",
LONGFORM = "longForm",
BOOLEAN = "boolean",
NUMBER = "number",
PASSWORD = "password",
LIST = "list",
OBJECT = "object",
JSON = "json",
FILE = "file",
}
export enum SourceNames {

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEQTCCAqmgAwIBAgIUMW63R07yQUF/38L/MpYzsfZd1iUwDQYJKoZIhvcNAQEM
BQAwOjE4MDYGA1UEAwwvNzNjZWI4ZTAtZGE3MC00MzIxLWIyOTEtYjM1Mzk0ZWMw
NDhiIFByb2plY3QgQ0EwHhcNMjEwNTE3MDU0MTQ0WhcNMzEwNTE1MDU0MTQ0WjA6
MTgwNgYDVQQDDC83M2NlYjhlMC1kYTcwLTQzMjEtYjI5MS1iMzUzOTRlYzA0OGIg
UHJvamVjdCBDQTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBANNpSowg
GurJ0hE4/Lgmg4G3u0AuUqKEsIXikjLl/vTDZV4tzPDgXtrzyaXeI6BgS4/Rcyxg
KmGCMJIqaiUI/svSz7zMB4jJefBPRDm6CGU3KjoqNR84DYUZHLoRujvLm8WYROUL
qHgm3EN4OAkifoO1wqqQk6rZR+EMDF+WKQ38wsE9bCm+lYjpPGY4MOoqKZtxtj8H
1UnH05CC6I0Cv1GUKHYxFqqHhwt5ICa0MSNR6PZGvJXgZUj1uo0XhwEXrZHtbh22
U0vVLgaP/85YcpO6Q7qHL249yHsKF9rGZqlSzCHg5hWe+uzzwFAd7LH4D61LRBYk
xGjCK9vzIjylIRRhlpesA9RHUWCvFe7Tw6YLQqrMGnbBYfEVUW2peFy9y7CuZLTb
persBctuICq4UlBDFhy8zsjCSOkfy9jZIx9Se4+C4yMIl6st7x8jlozOeDHIxSvR
REcJUIy0udQ6AkCoccMHM/qh+IggsD4ubSUsBKDMcyYTWzsiIesbidhC/wIDAQAB
oz8wPTAdBgNVHQ4EFgQUpHa3jpKqkIYen1YiWtXuUDcJ/CUwDwYDVR0TBAgwBgEB
/wIBADALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEMBQADggGBABUe16pNHSF9ZlK5
V9Y45T7qI526zKOf1zUqZOtX35EaQRZ6yvwO4yl1B6BoFk1v1w8jFCmpRNwkBDYr
uGVfZ+mBiAF6Djgrkw5Yd5atLtsk8jLHzBk00gEt9VlAQgfivc+s9kRqig4dOG25
yiHNChRgMTvifQauXq71/5L6N1sgE8XEljj5/kY5C/YeM5/52ja/Bx5mHY5qtxxF
7+CIpUyTZSxuJUPp1F98tpTRiuJDIK60ahmFmvEUQthVZlAx1XzOidATeTtUEnvw
CVmQyabgp5ewmRNjER4DRJpbpRzf1UUrGxjVHCx8/mn6nf2or0AtsSNoFPGugU1z
hSqHk3SmEC8uSBVCLZuyIG496OE0RYIC7KG7Lg3X3UHCBHngpCKoA+V1z5P5kPg3
CPpNvQOuNIEpOEUPgjjbxbVDHsvZH7ix8OZ31K8ioEs9SvXXAW3fSnHxqEBe2LjN
zHHBiQEJSyL1u4Nx/NahJtP22DUfF6uHdHtpdhxEKVW6GvhNcw==
-----END CERTIFICATE-----

View File

@ -28,6 +28,8 @@ module PostgresModule {
user: string
password: string
ssl?: boolean
ca?: string
rejectUnauthorized?: boolean
}
const SCHEMA: Integration = {
@ -67,6 +69,16 @@ module PostgresModule {
default: false,
required: false,
},
rejectUnauthorized: {
type: DatasourceFieldTypes.BOOLEAN,
default: false,
required: false,
},
ca: {
type: DatasourceFieldTypes.LONGFORM,
default: false,
required: false,
},
},
query: {
create: {
@ -144,7 +156,13 @@ module PostgresModule {
let newConfig = {
...this.config,
ssl: this.config.ssl ? { rejectUnauthorized: true } : undefined,
// ssl: this.config.ssl ? { rejectUnauthorized: true } : undefined,
ssl: this.config.ssl
? {
rejectUnauthorized: this.config.rejectUnauthorized,
ca: this.config.ca,
}
: undefined,
}
if (!this.pool) {
this.pool = new Pool(newConfig)

View File

@ -14,6 +14,7 @@ const {
isMultiTenant,
} = require("@budibase/auth/tenancy")
const env = require("../../../environment")
const { endSession } = require("../../../../../auth/sessions")
function googleCallbackUrl(config) {
// incase there is a callback URL from before
@ -121,8 +122,10 @@ exports.resetUpdate = async ctx => {
}
exports.logout = async ctx => {
const authCookie = getCookie(ctx, Cookies.Auth)
clearCookie(ctx, Cookies.Auth)
clearCookie(ctx, Cookies.CurrentApp)
await endSession(authCookie.sessionId)
ctx.body = { message: "User logged out." }
}

View File

@ -171,7 +171,9 @@ exports.updateSelf = async ctx => {
const db = getGlobalDB()
const user = await db.get(ctx.user._id)
if (ctx.request.body.password) {
// changing password
ctx.request.body.password = await hash(ctx.request.body.password)
await invalidateSessions(ctx.user._id)
}
// don't allow sending up an ID/Rev, always use the existing one
delete ctx.request.body._id