Updating datasource API to remove secrets when returning datasources.

This commit is contained in:
mike12345567 2023-01-18 19:16:19 +00:00
parent a6a42f4d2e
commit b659060beb
5 changed files with 81 additions and 27 deletions

View File

@ -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>

View File

@ -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) => {

View File

@ -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)

View File

@ -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
}

View File

@ -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
} }