Updating datasource API to remove secrets when returning datasources.
This commit is contained in:
parent
a6a42f4d2e
commit
b659060beb
|
@ -53,7 +53,7 @@
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<Heading size="M">Envrironment Variables</Heading>
|
<Heading size="M">Environment Variables</Heading>
|
||||||
{#if !$licensing.environmentVariablesEnabled}
|
{#if !$licensing.environmentVariablesEnabled}
|
||||||
<Tags>
|
<Tags>
|
||||||
<Tag icon="LockClosed">Pro plan</Tag>
|
<Tag icon="LockClosed">Pro plan</Tag>
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function createDatasourcesStore() {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateDatasource = async response => {
|
const updateDatasource = response => {
|
||||||
const { datasource, error } = response
|
const { datasource, error } = response
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const currentIdx = state.list.findIndex(ds => ds._id === datasource._id)
|
const currentIdx = state.list.findIndex(ds => ds._id === datasource._id)
|
||||||
|
@ -52,7 +52,7 @@ export function createDatasourcesStore() {
|
||||||
datasourceId: datasource?._id,
|
datasourceId: datasource?._id,
|
||||||
tablesFilter,
|
tablesFilter,
|
||||||
})
|
})
|
||||||
return await updateDatasource(response)
|
return updateDatasource(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
const save = async (body, fetchSchema = false) => {
|
const save = async (body, fetchSchema = false) => {
|
||||||
|
|
|
@ -8,18 +8,24 @@ import {
|
||||||
} from "../../db/utils"
|
} from "../../db/utils"
|
||||||
import { destroy as tableDestroy } from "./table/internal"
|
import { destroy as tableDestroy } from "./table/internal"
|
||||||
import { BuildSchemaErrors, InvalidColumns } from "../../constants"
|
import { BuildSchemaErrors, InvalidColumns } from "../../constants"
|
||||||
import { getIntegration } from "../../integrations"
|
import { getIntegration, getDefinitions } from "../../integrations"
|
||||||
import { getDatasourceAndQuery } from "./row/utils"
|
import { getDatasourceAndQuery } from "./row/utils"
|
||||||
import { invalidateDynamicVariables } from "../../threads/utils"
|
import { invalidateDynamicVariables } from "../../threads/utils"
|
||||||
import { db as dbCore, context, events } from "@budibase/backend-core"
|
import { db as dbCore, context, events } from "@budibase/backend-core"
|
||||||
import { BBContext, Datasource, Row } from "@budibase/types"
|
import {
|
||||||
|
UserCtx,
|
||||||
|
Datasource,
|
||||||
|
Row,
|
||||||
|
DatasourceFieldType,
|
||||||
|
PASSWORD_REPLACEMENT,
|
||||||
|
} from "@budibase/types"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { removeSecrets } from "../../sdk/app/datasources/datasources"
|
||||||
import { enrich } from "../../sdk/app/datasources/datasources"
|
|
||||||
|
|
||||||
export async function fetch(ctx: BBContext) {
|
export async function fetch(ctx: UserCtx) {
|
||||||
// Get internal tables
|
// Get internal tables
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
|
const definitions = await getDefinitions()
|
||||||
const internalTables = await db.allDocs(
|
const internalTables = await db.allDocs(
|
||||||
getTableParams(null, {
|
getTableParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -46,23 +52,19 @@ export async function fetch(ctx: BBContext) {
|
||||||
)
|
)
|
||||||
).rows.map(row => row.doc)
|
).rows.map(row => row.doc)
|
||||||
|
|
||||||
const allDatasources = [bbInternalDb, ...datasources]
|
const allDatasources: Datasource[] = [bbInternalDb, ...datasources]
|
||||||
|
|
||||||
for (let datasource of allDatasources) {
|
for (let datasource of allDatasources) {
|
||||||
if (datasource.config && datasource.config.auth) {
|
datasource = sdk.datasources.removeSecrets(definitions, datasource)
|
||||||
// strip secrets from response so they don't show in the network request
|
|
||||||
delete datasource.config.auth
|
|
||||||
}
|
|
||||||
|
|
||||||
if (datasource.type === dbCore.BUDIBASE_DATASOURCE_TYPE) {
|
if (datasource.type === dbCore.BUDIBASE_DATASOURCE_TYPE) {
|
||||||
datasource.entities = internal[datasource._id]
|
datasource.entities = internal[datasource._id!]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = [bbInternalDb, ...datasources]
|
ctx.body = [bbInternalDb, ...datasources]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function buildSchemaFromDb(ctx: BBContext) {
|
export async function buildSchemaFromDb(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasource = await sdk.datasources.get(ctx.params.datasourceId)
|
const datasource = await sdk.datasources.get(ctx.params.datasourceId)
|
||||||
const tablesFilter = ctx.request.body.tablesFilter
|
const tablesFilter = ctx.request.body.tablesFilter
|
||||||
|
@ -149,11 +151,12 @@ async function invalidateVariables(
|
||||||
await invalidateDynamicVariables(toInvalidate)
|
await invalidateDynamicVariables(toInvalidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update(ctx: BBContext) {
|
export async function update(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasourceId = ctx.params.datasourceId
|
const datasourceId = ctx.params.datasourceId
|
||||||
let datasource = await sdk.datasources.get(datasourceId)
|
let datasource = await sdk.datasources.get(datasourceId)
|
||||||
const auth = datasource.config?.auth
|
const auth = datasource.config?.auth
|
||||||
|
const definitions = await getDefinitions()
|
||||||
await invalidateVariables(datasource, ctx.request.body)
|
await invalidateVariables(datasource, ctx.request.body)
|
||||||
|
|
||||||
const isBudibaseSource = datasource.type === dbCore.BUDIBASE_DATASOURCE_TYPE
|
const isBudibaseSource = datasource.type === dbCore.BUDIBASE_DATASOURCE_TYPE
|
||||||
|
@ -182,13 +185,16 @@ export async function update(ctx: BBContext) {
|
||||||
|
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.message = "Datasource saved successfully."
|
ctx.message = "Datasource saved successfully."
|
||||||
ctx.body = { datasource }
|
ctx.body = {
|
||||||
|
datasource: sdk.datasources.removeSecrets(definitions, datasource),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(ctx: BBContext) {
|
export async function save(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const plus = ctx.request.body.datasource.plus
|
const plus = ctx.request.body.datasource.plus
|
||||||
const fetchSchema = ctx.request.body.fetchSchema
|
const fetchSchema = ctx.request.body.fetchSchema
|
||||||
|
const definitions = await getDefinitions()
|
||||||
|
|
||||||
const datasource = {
|
const datasource = {
|
||||||
_id: generateDatasourceID({ plus }),
|
_id: generateDatasourceID({ plus }),
|
||||||
|
@ -216,7 +222,9 @@ export async function save(ctx: BBContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response: any = { datasource }
|
const response: any = {
|
||||||
|
datasource: sdk.datasources.removeSecrets(definitions, datasource),
|
||||||
|
}
|
||||||
if (schemaError) {
|
if (schemaError) {
|
||||||
response.error = schemaError
|
response.error = schemaError
|
||||||
}
|
}
|
||||||
|
@ -254,7 +262,7 @@ async function destroyInternalTablesBySourceId(datasourceId: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function destroy(ctx: BBContext) {
|
export async function destroy(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasourceId = ctx.params.datasourceId
|
const datasourceId = ctx.params.datasourceId
|
||||||
|
|
||||||
|
@ -282,13 +290,15 @@ export async function destroy(ctx: BBContext) {
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function find(ctx: BBContext) {
|
export async function find(ctx: UserCtx) {
|
||||||
const database = context.getAppDB()
|
const database = context.getAppDB()
|
||||||
ctx.body = await database.get(ctx.params.datasourceId)
|
const definitions = await getDefinitions()
|
||||||
|
const datasource = await database.get(ctx.params.datasourceId)
|
||||||
|
ctx.body = sdk.datasources.removeSecrets(definitions, datasource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dynamic query functionality
|
// dynamic query functionality
|
||||||
export async function query(ctx: BBContext) {
|
export async function query(ctx: UserCtx) {
|
||||||
const queryJson = ctx.request.body
|
const queryJson = ctx.request.body
|
||||||
try {
|
try {
|
||||||
ctx.body = await getDatasourceAndQuery(queryJson)
|
ctx.body = await getDatasourceAndQuery(queryJson)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { processObjectSync } from "@budibase/string-templates"
|
import { processObjectSync, findHBSBlocks } from "@budibase/string-templates"
|
||||||
import { Datasource } from "@budibase/types"
|
import {
|
||||||
|
Datasource,
|
||||||
|
DatasourceFieldType,
|
||||||
|
Integration,
|
||||||
|
PASSWORD_REPLACEMENT,
|
||||||
|
} from "@budibase/types"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { getEnvironmentVariables } from "../../utils"
|
import { getEnvironmentVariables } from "../../utils"
|
||||||
|
|
||||||
|
@ -37,3 +42,31 @@ export async function getWithEnvVars(datasourceId: string) {
|
||||||
const datasource = await appDb.get(datasourceId)
|
const datasource = await appDb.get(datasourceId)
|
||||||
return enrichDatasourceWithValues(datasource)
|
return enrichDatasourceWithValues(datasource)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function removeSecrets(
|
||||||
|
definitions: Record<string, Integration>,
|
||||||
|
datasource: Datasource
|
||||||
|
) {
|
||||||
|
const schema = definitions[datasource.source]
|
||||||
|
if (datasource.config) {
|
||||||
|
// strip secrets from response, so they don't show in the network request
|
||||||
|
if (datasource.config.auth) {
|
||||||
|
delete datasource.config.auth
|
||||||
|
}
|
||||||
|
// remove passwords
|
||||||
|
for (let key of Object.keys(datasource.config)) {
|
||||||
|
if (typeof datasource.config[key] !== "string") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const blocks = findHBSBlocks(datasource.config[key] as string)
|
||||||
|
const usesEnvVars = blocks.find(block => block.includes("env.")) != null
|
||||||
|
if (
|
||||||
|
!usesEnvVars &&
|
||||||
|
schema.datasource?.[key]?.type === DatasourceFieldType.PASSWORD
|
||||||
|
) {
|
||||||
|
datasource.config[key] = PASSWORD_REPLACEMENT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return datasource
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { Table } from "../documents"
|
import { Table } from "../documents"
|
||||||
|
|
||||||
|
export const PASSWORD_REPLACEMENT = "--secret-value--"
|
||||||
|
|
||||||
export enum Operation {
|
export enum Operation {
|
||||||
CREATE = "CREATE",
|
CREATE = "CREATE",
|
||||||
READ = "READ",
|
READ = "READ",
|
||||||
|
@ -104,7 +106,16 @@ export interface Integration {
|
||||||
friendlyName: string
|
friendlyName: string
|
||||||
type?: string
|
type?: string
|
||||||
iconUrl?: string
|
iconUrl?: string
|
||||||
datasource: {}
|
datasource: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
type: string
|
||||||
|
display?: string
|
||||||
|
deprecated?: boolean
|
||||||
|
default?: any
|
||||||
|
required?: boolean
|
||||||
|
}
|
||||||
|
>
|
||||||
query: {
|
query: {
|
||||||
[key: string]: QueryDefinition
|
[key: string]: QueryDefinition
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue