SSL support for digitalocean, started utility function for BB logout, bunch of minor bug fixes
This commit is contained in:
parent
5ae5d8ddf0
commit
14370c81b9
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "../.."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../../../budibase-infra"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -42,8 +42,9 @@ module.exports = (
|
||||||
internal = false
|
internal = false
|
||||||
if (authCookie) {
|
if (authCookie) {
|
||||||
let error = null
|
let error = null
|
||||||
const sessionId = authCookie.sessionId,
|
const sessionId = authCookie.sessionId
|
||||||
userId = authCookie.userId
|
const userId = authCookie.userId
|
||||||
|
|
||||||
const session = await getSession(userId, sessionId)
|
const session = await getSession(userId, sessionId)
|
||||||
if (!session) {
|
if (!session) {
|
||||||
error = "No session found"
|
error = "No session found"
|
||||||
|
|
|
@ -24,17 +24,24 @@ exports.createASession = async (userId, session) => {
|
||||||
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS)
|
await client.store(makeSessionID(userId, sessionId), session, EXPIRY_SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.invalidateSessions = async (userId, sessionId = null) => {
|
exports.invalidateSessions = async (userId, sessionIds = null) => {
|
||||||
let sessions = []
|
let sessions = []
|
||||||
if (sessionId) {
|
|
||||||
sessions.push({ key: makeSessionID(userId, sessionId) })
|
// If no sessionIds, get all the sessions for the user
|
||||||
} else {
|
if (!sessionIds) {
|
||||||
sessions = await getSessionsForUser(userId)
|
sessions = await getSessionsForUser(userId)
|
||||||
sessions.forEach(
|
sessions.forEach(
|
||||||
session =>
|
session =>
|
||||||
(session.key = makeSessionID(session.userId, session.sessionId))
|
(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 client = await redis.getSessionClient()
|
||||||
const promises = []
|
const promises = []
|
||||||
for (let session of sessions) {
|
for (let session of sessions) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ const {
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const { options } = require("./middleware/passport/jwt")
|
const { options } = require("./middleware/passport/jwt")
|
||||||
const { createUserEmailView } = require("./db/views")
|
const { createUserEmailView } = require("./db/views")
|
||||||
const { Headers, UserStatus } = require("./constants")
|
const { Headers, UserStatus, Cookies } = require("./constants")
|
||||||
const {
|
const {
|
||||||
getGlobalDB,
|
getGlobalDB,
|
||||||
updateTenantId,
|
updateTenantId,
|
||||||
|
@ -19,6 +19,10 @@ const accounts = require("./cloud/accounts")
|
||||||
const { hash } = require("./hashing")
|
const { hash } = require("./hashing")
|
||||||
const userCache = require("./cache/user")
|
const userCache = require("./cache/user")
|
||||||
const env = require("./environment")
|
const env = require("./environment")
|
||||||
|
const {
|
||||||
|
getSessionsForUser,
|
||||||
|
invalidateSessions,
|
||||||
|
} = require("./security/sessions")
|
||||||
|
|
||||||
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// ***********************************************
|
// ***********************************************
|
||||||
//
|
//
|
||||||
|
|
||||||
Cypress.on('uncaught:exception', (err, runnable) => {
|
Cypress.on("uncaught:exception", () => {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
const FORMULA_TYPE = FIELDS.FORMULA.type
|
const FORMULA_TYPE = FIELDS.FORMULA.type
|
||||||
const LINK_TYPE = FIELDS.LINK.type
|
const LINK_TYPE = FIELDS.LINK.type
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev"]
|
||||||
const { hide } = getContext(Context.Modal)
|
const { hide } = getContext(Context.Modal)
|
||||||
let fieldDefinitions = cloneDeep(FIELDS)
|
let fieldDefinitions = cloneDeep(FIELDS)
|
||||||
|
|
||||||
|
@ -66,6 +67,10 @@
|
||||||
(field.type === LINK_TYPE && !field.tableId) ||
|
(field.type === LINK_TYPE && !field.tableId) ||
|
||||||
Object.keys($tables.draft?.schema ?? {}).some(
|
Object.keys($tables.draft?.schema ?? {}).some(
|
||||||
key => key !== originalName && key === field.name
|
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
|
// used to select what different options can be displayed for column type
|
||||||
|
@ -200,6 +205,9 @@
|
||||||
label="Name"
|
label="Name"
|
||||||
bind:value={field.name}
|
bind:value={field.name}
|
||||||
disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)}
|
disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)}
|
||||||
|
error={columnNameInvalid
|
||||||
|
? "type, _id and _rev are disallowed as column names"
|
||||||
|
: ""}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
<script>
|
<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 KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
export let integration
|
export let integration
|
||||||
export let schema
|
export let schema
|
||||||
|
|
||||||
let addButton
|
let addButton
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -29,6 +37,15 @@
|
||||||
<Label>{capitalise(configKey)}</Label>
|
<Label>{capitalise(configKey)}</Label>
|
||||||
<Toggle text="" bind:value={integration[configKey]} />
|
<Toggle text="" bind:value={integration[configKey]} />
|
||||||
</div>
|
</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}
|
{:else}
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<Label>{capitalise(configKey)}</Label>
|
<Label>{capitalise(configKey)}</Label>
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={externalDatasourceModal}>
|
<Modal bind:this={externalDatasourceModal}>
|
||||||
<DatasourceConfigModal {integration} />
|
<DatasourceConfigModal {integration} {modal} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
? "Fetch tables from database"
|
? "Fetch tables from database"
|
||||||
: "Save and continue to query"}
|
: "Save and continue to query"}
|
||||||
cancelText="Back"
|
cancelText="Back"
|
||||||
size="M"
|
size="L"
|
||||||
>
|
>
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Body size="XS"
|
<Body size="XS"
|
||||||
|
|
|
@ -77,7 +77,7 @@ exports.run = async function ({ inputs }) {
|
||||||
const { status, message } = await getFetchResponse(response)
|
const { status, message } = await getFetchResponse(response)
|
||||||
return {
|
return {
|
||||||
httpStatus: status,
|
httpStatus: status,
|
||||||
success: status === 200,
|
success: status === 200 || status === 204,
|
||||||
response: message,
|
response: message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,14 @@ export enum QueryTypes {
|
||||||
|
|
||||||
export enum DatasourceFieldTypes {
|
export enum DatasourceFieldTypes {
|
||||||
STRING = "string",
|
STRING = "string",
|
||||||
|
LONGFORM = "longForm",
|
||||||
BOOLEAN = "boolean",
|
BOOLEAN = "boolean",
|
||||||
NUMBER = "number",
|
NUMBER = "number",
|
||||||
PASSWORD = "password",
|
PASSWORD = "password",
|
||||||
LIST = "list",
|
LIST = "list",
|
||||||
OBJECT = "object",
|
OBJECT = "object",
|
||||||
JSON = "json",
|
JSON = "json",
|
||||||
|
FILE = "file",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SourceNames {
|
export enum SourceNames {
|
||||||
|
|
|
@ -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-----
|
|
@ -28,6 +28,8 @@ module PostgresModule {
|
||||||
user: string
|
user: string
|
||||||
password: string
|
password: string
|
||||||
ssl?: boolean
|
ssl?: boolean
|
||||||
|
ca?: string
|
||||||
|
rejectUnauthorized?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const SCHEMA: Integration = {
|
const SCHEMA: Integration = {
|
||||||
|
@ -67,6 +69,16 @@ module PostgresModule {
|
||||||
default: false,
|
default: false,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
rejectUnauthorized: {
|
||||||
|
type: DatasourceFieldTypes.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
ca: {
|
||||||
|
type: DatasourceFieldTypes.LONGFORM,
|
||||||
|
default: false,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
|
@ -144,7 +156,13 @@ module PostgresModule {
|
||||||
|
|
||||||
let newConfig = {
|
let newConfig = {
|
||||||
...this.config,
|
...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) {
|
if (!this.pool) {
|
||||||
this.pool = new Pool(newConfig)
|
this.pool = new Pool(newConfig)
|
||||||
|
|
|
@ -14,6 +14,7 @@ const {
|
||||||
isMultiTenant,
|
isMultiTenant,
|
||||||
} = require("@budibase/auth/tenancy")
|
} = require("@budibase/auth/tenancy")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
|
const { endSession } = require("../../../../../auth/sessions")
|
||||||
|
|
||||||
function googleCallbackUrl(config) {
|
function googleCallbackUrl(config) {
|
||||||
// incase there is a callback URL from before
|
// incase there is a callback URL from before
|
||||||
|
@ -121,8 +122,10 @@ exports.resetUpdate = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.logout = async ctx => {
|
exports.logout = async ctx => {
|
||||||
|
const authCookie = getCookie(ctx, Cookies.Auth)
|
||||||
clearCookie(ctx, Cookies.Auth)
|
clearCookie(ctx, Cookies.Auth)
|
||||||
clearCookie(ctx, Cookies.CurrentApp)
|
clearCookie(ctx, Cookies.CurrentApp)
|
||||||
|
await endSession(authCookie.sessionId)
|
||||||
ctx.body = { message: "User logged out." }
|
ctx.body = { message: "User logged out." }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,9 @@ exports.updateSelf = async ctx => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
const user = await db.get(ctx.user._id)
|
const user = await db.get(ctx.user._id)
|
||||||
if (ctx.request.body.password) {
|
if (ctx.request.body.password) {
|
||||||
|
// changing password
|
||||||
ctx.request.body.password = await hash(ctx.request.body.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
|
// don't allow sending up an ID/Rev, always use the existing one
|
||||||
delete ctx.request.body._id
|
delete ctx.request.body._id
|
||||||
|
|
Loading…
Reference in New Issue