Merge pull request #15769 from Budibase/BUDI-9127/validate

Validate config
This commit is contained in:
Adria Navarro 2025-03-19 18:25:10 +01:00 committed by GitHub
commit 67543dbe7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 111 additions and 7 deletions

View File

@ -49,5 +49,5 @@
</ActionMenu>
<Modal bind:this={modal}>
<OAuth2ConfigModalContent config={row} />
<OAuth2ConfigModalContent config={{ ...row }} />
</Modal>

View File

@ -70,12 +70,28 @@
return keepOpen
}
const { data: configData } = validationResult
try {
const connectionValidation = await oauth2.validate({
id: config?.id,
url: configData.url,
clientId: configData.clientId,
clientSecret: configData.clientSecret,
})
if (!connectionValidation.valid) {
let message = "Connection settings could not be validated"
if (connectionValidation.message) {
message += `: ${connectionValidation.message}`
}
notifications.error(message)
return keepOpen
}
if (isCreation) {
await oauth2.create(validationResult.data)
await oauth2.create(configData)
notifications.success("Settings created.")
} else {
await oauth2.edit(config!.id, validationResult.data)
await oauth2.edit(config!.id, configData)
notifications.success("Settings saved.")
}
} catch (e: any) {

View File

@ -1,6 +1,7 @@
import { API } from "@/api"
import { BudiStore } from "@/stores/BudiStore"
import { OAuth2Config, UpsertOAuth2Config } from "@/types"
import { ValidateConfigRequest } from "@budibase/types"
interface OAuth2StoreState {
configs: OAuth2Config[]
@ -57,6 +58,10 @@ export class OAuth2Store extends BudiStore<OAuth2StoreState> {
await API.oauth2.delete(id)
await this.fetch()
}
async validate(config: ValidateConfigRequest) {
return await API.oauth2.validate(config)
}
}
export const oauth2 = new OAuth2Store()

View File

@ -3,6 +3,8 @@ import {
OAuth2ConfigResponse,
UpsertOAuth2ConfigRequest,
UpsertOAuth2ConfigResponse,
ValidateConfigRequest,
ValidateConfigResponse,
} from "@budibase/types"
import { BaseAPIClient } from "./types"
@ -16,6 +18,7 @@ export interface OAuth2Endpoints {
config: UpsertOAuth2ConfigRequest
) => Promise<UpsertOAuth2ConfigResponse>
delete: (id: string) => Promise<void>
validate: (config: ValidateConfigRequest) => Promise<ValidateConfigResponse>
}
export const buildOAuth2Endpoints = (API: BaseAPIClient): OAuth2Endpoints => ({
@ -32,8 +35,6 @@ export const buildOAuth2Endpoints = (API: BaseAPIClient): OAuth2Endpoints => ({
/**
* Creates a OAuth2 configuration.
* @param name the name of the row action
* @param tableId the ID of the table
*/
create: async config => {
return await API.post<
@ -49,8 +50,6 @@ export const buildOAuth2Endpoints = (API: BaseAPIClient): OAuth2Endpoints => ({
/**
* Updates an existing OAuth2 configuration.
* @param name the name of the row action
* @param tableId the ID of the table
*/
update: async (id, config) => {
return await API.put<UpsertOAuth2ConfigRequest, UpsertOAuth2ConfigResponse>(
@ -72,4 +71,14 @@ export const buildOAuth2Endpoints = (API: BaseAPIClient): OAuth2Endpoints => ({
url: `/api/oauth2/${id}`,
})
},
validate: async function (
config: ValidateConfigRequest
): Promise<ValidateConfigResponse> {
return await API.post<ValidateConfigRequest, ValidateConfigResponse>({
url: `/api/oauth2/validate`,
body: {
...config,
},
})
},
})

View File

@ -7,6 +7,8 @@ import {
RequiredKeys,
OAuth2ConfigResponse,
PASSWORD_REPLACEMENT,
ValidateConfigResponse,
ValidateConfigRequest,
} from "@budibase/types"
import sdk from "../../sdk"
@ -75,3 +77,27 @@ export async function remove(
await sdk.oauth2.remove(configToRemove)
ctx.status = 204
}
export async function validate(
ctx: Ctx<ValidateConfigRequest, ValidateConfigResponse>
) {
const { body } = ctx.request
const config = {
url: body.url,
clientId: body.clientId,
clientSecret: body.clientSecret,
}
if (config.clientSecret === PASSWORD_REPLACEMENT && body.id) {
const existingConfig = await sdk.oauth2.get(body.id)
if (!existingConfig) {
ctx.throw(`OAuth2 config with id '${body.id}' not found.`, 404)
}
config.clientSecret = existingConfig.clientSecret
}
const validation = await sdk.oauth2.validateConfig(config)
ctx.status = 201
ctx.body = validation
}

View File

@ -38,5 +38,10 @@ router.delete(
authorized(PermissionType.BUILDER),
controller.remove
)
router.post(
"/api/oauth2/validate",
authorized(PermissionType.BUILDER),
controller.validate
)
export default router

View File

@ -31,3 +31,34 @@ export async function generateToken(id: string) {
return `${jsonResponse.token_type} ${jsonResponse.access_token}`
}
export async function validateConfig(config: {
url: string
clientId: string
clientSecret: string
}): Promise<{ valid: boolean; message?: string }> {
try {
const resp = await fetch(config.url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: config.clientId,
client_secret: config.clientSecret,
}),
redirect: "follow",
})
const jsonResponse = await resp.json()
if (!resp.ok) {
const message = jsonResponse.error_description ?? resp.statusText
return { valid: false, message }
}
return { valid: true }
} catch (e: any) {
return { valid: false, message: e.message }
}
}

View File

@ -20,3 +20,15 @@ export interface UpsertOAuth2ConfigRequest {
export interface UpsertOAuth2ConfigResponse {
config: OAuth2ConfigResponse
}
export interface ValidateConfigRequest {
id?: string
url: string
clientId: string
clientSecret: string
}
export interface ValidateConfigResponse {
valid: boolean
message?: string
}