Update to cover the authentication for REST, replacing it with secret value and converting to password field, as well as minor update to env dropdown to only be a password field when not containing an environment variable - #9480.
This commit is contained in:
parent
15d0c73eb4
commit
c2eb8fb976
7 changed files with 105 additions and 67 deletions
|
@ -134,7 +134,7 @@
|
|||
on:blur={onBlur}
|
||||
on:focus={onFocus}
|
||||
on:input={onInput}
|
||||
{type}
|
||||
type={hbsValue.length ? "text" : type}
|
||||
style={align ? `text-align: ${align};` : ""}
|
||||
class="spectrum-Textfield-input"
|
||||
inputmode={type === "number" ? "decimal" : "text"}
|
||||
|
|
|
@ -248,6 +248,7 @@
|
|||
/>
|
||||
<EnvDropdown
|
||||
label="Password"
|
||||
type="password"
|
||||
bind:value={form.basic.password}
|
||||
on:change={onFieldChange}
|
||||
on:blur={() => (blurred.basic.password = true)}
|
||||
|
|
|
@ -7,44 +7,3 @@
|
|||
export interface QueryOptions {
|
||||
disableReturning?: boolean
|
||||
}
|
||||
|
||||
export enum AuthType {
|
||||
BASIC = "basic",
|
||||
BEARER = "bearer",
|
||||
}
|
||||
|
||||
interface AuthConfig {
|
||||
_id: string
|
||||
name: string
|
||||
type: AuthType
|
||||
config: BasicAuthConfig | BearerAuthConfig
|
||||
}
|
||||
|
||||
export interface BasicAuthConfig {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface BearerAuthConfig {
|
||||
token: string
|
||||
}
|
||||
|
||||
export interface RestConfig {
|
||||
url: string
|
||||
rejectUnauthorized: boolean
|
||||
defaultHeaders: {
|
||||
[key: string]: any
|
||||
}
|
||||
legacyHttpParser: boolean
|
||||
authConfigs: AuthConfig[]
|
||||
staticVariables: {
|
||||
[key: string]: string
|
||||
}
|
||||
dynamicVariables: [
|
||||
{
|
||||
name: string
|
||||
queryId: string
|
||||
value: string
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,13 +6,11 @@ import {
|
|||
IntegrationBase,
|
||||
PaginationValues,
|
||||
RestQueryFields as RestQuery,
|
||||
} from "@budibase/types"
|
||||
import {
|
||||
RestConfig,
|
||||
AuthType,
|
||||
BasicAuthConfig,
|
||||
BearerAuthConfig,
|
||||
} from "../definitions/datasource"
|
||||
RestAuthType,
|
||||
RestBasicAuthConfig,
|
||||
RestBearerAuthConfig,
|
||||
} from "@budibase/types"
|
||||
import { get } from "lodash"
|
||||
import * as https from "https"
|
||||
import qs from "querystring"
|
||||
|
@ -331,14 +329,14 @@ class RestIntegration implements IntegrationBase {
|
|||
if (authConfig) {
|
||||
let config
|
||||
switch (authConfig.type) {
|
||||
case AuthType.BASIC:
|
||||
config = authConfig.config as BasicAuthConfig
|
||||
case RestAuthType.BASIC:
|
||||
config = authConfig.config as RestBasicAuthConfig
|
||||
headers.Authorization = `Basic ${Buffer.from(
|
||||
`${config.username}:${config.password}`
|
||||
).toString("base64")}`
|
||||
break
|
||||
case AuthType.BEARER:
|
||||
config = authConfig.config as BearerAuthConfig
|
||||
case RestAuthType.BEARER:
|
||||
config = authConfig.config as RestBearerAuthConfig
|
||||
headers.Authorization = `Bearer ${config.token}`
|
||||
break
|
||||
}
|
||||
|
@ -428,5 +426,4 @@ class RestIntegration implements IntegrationBase {
|
|||
export default {
|
||||
schema: SCHEMA,
|
||||
integration: RestIntegration,
|
||||
AuthType,
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ jest.mock("node-fetch", () => {
|
|||
|
||||
import fetch from "node-fetch"
|
||||
import { default as RestIntegration } from "../rest"
|
||||
import { RestAuthType } from "@budibase/types"
|
||||
const FormData = require("form-data")
|
||||
const { URLSearchParams } = require("url")
|
||||
|
||||
|
@ -229,7 +230,7 @@ describe("REST Integration", () => {
|
|||
const basicAuth = {
|
||||
_id: "c59c14bd1898a43baa08da68959b24686",
|
||||
name: "basic-1",
|
||||
type: RestIntegration.AuthType.BASIC,
|
||||
type: RestAuthType.BASIC,
|
||||
config: {
|
||||
username: "user",
|
||||
password: "password",
|
||||
|
@ -239,7 +240,7 @@ describe("REST Integration", () => {
|
|||
const bearerAuth = {
|
||||
_id: "0d91d732f34e4befabeff50b392a8ff3",
|
||||
name: "bearer-1",
|
||||
type: RestIntegration.AuthType.BEARER,
|
||||
type: RestAuthType.BEARER,
|
||||
config: {
|
||||
token: "mytoken",
|
||||
},
|
||||
|
@ -581,6 +582,7 @@ describe("REST Integration", () => {
|
|||
})
|
||||
await config.integration.read({})
|
||||
|
||||
// @ts-ignore
|
||||
const calls: any = fetch.mock.calls[0]
|
||||
const url = calls[0]
|
||||
expect(url).toBe(`${BASE_URL}/`)
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import { context } from "@budibase/backend-core"
|
||||
import { processObjectSync, findHBSBlocks } from "@budibase/string-templates"
|
||||
import { findHBSBlocks, processObjectSync } from "@budibase/string-templates"
|
||||
import {
|
||||
Datasource,
|
||||
DatasourceFieldType,
|
||||
Integration,
|
||||
PASSWORD_REPLACEMENT,
|
||||
RestAuthConfig,
|
||||
RestAuthType,
|
||||
RestBasicAuthConfig,
|
||||
SourceName,
|
||||
} from "@budibase/types"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import { getEnvironmentVariables } from "../../utils"
|
||||
import { getDefinitions } from "../../../integrations"
|
||||
|
||||
const ENV_VAR_PREFIX = "env."
|
||||
const USER_PREFIX = "user"
|
||||
|
||||
async function enrichDatasourceWithValues(datasource: Datasource) {
|
||||
const cloned = cloneDeep(datasource)
|
||||
|
@ -47,6 +49,18 @@ export async function getWithEnvVars(datasourceId: string) {
|
|||
return enrichDatasourceWithValues(datasource)
|
||||
}
|
||||
|
||||
function hasAuthConfigs(datasource: Datasource) {
|
||||
return datasource.source === SourceName.REST && datasource.config?.authConfigs
|
||||
}
|
||||
|
||||
function useEnvVars(str: any) {
|
||||
if (typeof str !== "string") {
|
||||
return false
|
||||
}
|
||||
const blocks = findHBSBlocks(str)
|
||||
return blocks.find(block => block.includes(ENV_VAR_PREFIX)) != null
|
||||
}
|
||||
|
||||
export async function removeSecrets(datasources: Datasource[]) {
|
||||
const definitions = await getDefinitions()
|
||||
for (let datasource of datasources) {
|
||||
|
@ -56,17 +70,24 @@ export async function removeSecrets(datasources: Datasource[]) {
|
|||
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
|
||||
// specific to REST datasources, contains passwords
|
||||
if (hasAuthConfigs(datasource)) {
|
||||
const configs = datasource.config.authConfigs as RestAuthConfig[]
|
||||
for (let config of configs) {
|
||||
if (config.type !== RestAuthType.BASIC) {
|
||||
continue
|
||||
}
|
||||
const basic = config.config as RestBasicAuthConfig
|
||||
if (!useEnvVars(basic.password)) {
|
||||
basic.password = PASSWORD_REPLACEMENT
|
||||
}
|
||||
}
|
||||
const blocks = findHBSBlocks(datasource.config[key] as string)
|
||||
const usesEnvVars =
|
||||
blocks.find(block => block.includes(ENV_VAR_PREFIX)) != null
|
||||
}
|
||||
// remove general passwords
|
||||
for (let key of Object.keys(datasource.config)) {
|
||||
if (
|
||||
!usesEnvVars &&
|
||||
schema.datasource?.[key]?.type === DatasourceFieldType.PASSWORD
|
||||
schema.datasource?.[key]?.type === DatasourceFieldType.PASSWORD &&
|
||||
!useEnvVars(datasource.config[key])
|
||||
) {
|
||||
datasource.config[key] = PASSWORD_REPLACEMENT
|
||||
}
|
||||
|
@ -84,6 +105,23 @@ export function mergeConfigs(update: Datasource, old: Datasource) {
|
|||
if (!update.config) {
|
||||
return update
|
||||
}
|
||||
// specific to REST datasources, fix the auth configs again if required
|
||||
if (hasAuthConfigs(update)) {
|
||||
const configs = update.config.authConfigs as RestAuthConfig[]
|
||||
const oldConfigs = old.config?.authConfigs as RestAuthConfig[]
|
||||
for (let config of configs) {
|
||||
if (config.type !== RestAuthType.BASIC) {
|
||||
continue
|
||||
}
|
||||
const basic = config.config as RestBasicAuthConfig
|
||||
const oldBasic = oldConfigs.find(old => old.name === config.name)
|
||||
?.config as RestBasicAuthConfig
|
||||
if (basic.password === PASSWORD_REPLACEMENT) {
|
||||
basic.password = oldBasic.password
|
||||
}
|
||||
}
|
||||
}
|
||||
// update back to actual passwords for everything else
|
||||
for (let [key, value] of Object.entries(update.config)) {
|
||||
if (value !== PASSWORD_REPLACEMENT) {
|
||||
continue
|
||||
|
|
|
@ -15,3 +15,44 @@ export interface Datasource extends Document {
|
|||
[key: string]: Table
|
||||
}
|
||||
}
|
||||
|
||||
export enum RestAuthType {
|
||||
BASIC = "basic",
|
||||
BEARER = "bearer",
|
||||
}
|
||||
|
||||
export interface RestBasicAuthConfig {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface RestBearerAuthConfig {
|
||||
token: string
|
||||
}
|
||||
|
||||
export interface RestAuthConfig {
|
||||
_id: string
|
||||
name: string
|
||||
type: RestAuthType
|
||||
config: RestBasicAuthConfig | RestBearerAuthConfig
|
||||
}
|
||||
|
||||
export interface RestConfig {
|
||||
url: string
|
||||
rejectUnauthorized: boolean
|
||||
defaultHeaders: {
|
||||
[key: string]: any
|
||||
}
|
||||
legacyHttpParser: boolean
|
||||
authConfigs: RestAuthConfig[]
|
||||
staticVariables: {
|
||||
[key: string]: string
|
||||
}
|
||||
dynamicVariables: [
|
||||
{
|
||||
name: string
|
||||
queryId: string
|
||||
value: string
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue