mirror of
https://github.com/harness/drone.git
synced 2025-05-07 07:39:03 +08:00

* feat: [AH-883]: refetch webhook after update and fix route to webhook details page from webhook list page * feat: [AH-883]: fix breaking css for inline inputs * feat: [AH-883]: implement Edit flow with enterprise secret input * feat: [AH-883]: Implement edit webhook page
272 lines
10 KiB
TypeScript
272 lines
10 KiB
TypeScript
/*
|
|
* Copyright 2024 Harness, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import produce from 'immer'
|
|
import * as Yup from 'yup'
|
|
import { compact, get, isEmpty, set } from 'lodash-es'
|
|
import type { AccessKeySecretKey, Anonymous, UserPassword } from '@harnessio/react-har-service-client'
|
|
|
|
import { Parent } from '@ar/common/types'
|
|
import { URL_REGEX } from '@ar/constants'
|
|
import type { Scope } from '@ar/MFEAppTypes'
|
|
import type { StringKeys } from '@ar/frameworks/strings'
|
|
import { SecretValueType } from '@ar/components/MultiTypeSecretInput/MultiTypeSecretInput'
|
|
|
|
import {
|
|
UpstreamRepositoryURLInputSource,
|
|
UpstreamProxyAuthenticationMode,
|
|
type UpstreamRegistryRequest
|
|
} from '../../types'
|
|
|
|
export function getSecretSpacePath(referenceString: string, scope?: Scope) {
|
|
if (!scope) return referenceString
|
|
if (referenceString.startsWith('account.')) {
|
|
return compact([scope.accountId]).join('/')
|
|
}
|
|
if (referenceString.startsWith('org.')) {
|
|
return compact([scope.accountId, scope.orgIdentifier]).join('/')
|
|
}
|
|
return compact([scope.accountId, scope.orgIdentifier, scope.projectIdentifier]).join('/')
|
|
}
|
|
|
|
export function getReferenceStringFromSecretSpacePath(identifier: string, secretSpacePath: string) {
|
|
const [accountId, orgIdentifier, projectIdentifier] = secretSpacePath.split('/')
|
|
if (projectIdentifier) return identifier
|
|
if (orgIdentifier) return `org.${identifier}`
|
|
if (accountId) return `account.${identifier}`
|
|
return identifier
|
|
}
|
|
|
|
function convertSecretInputToFormFields(
|
|
formData: UpstreamRegistryRequest,
|
|
secretField: string,
|
|
secretSpacePathField: string,
|
|
scope?: Scope
|
|
) {
|
|
const password = get(formData, secretField)
|
|
set(formData, secretSpacePathField, getSecretSpacePath(get(password, 'referenceString', ''), scope))
|
|
set(formData, secretField, get(password, 'identifier'))
|
|
}
|
|
|
|
function convertMultiTypeSecretInputToFormFields(
|
|
formData: UpstreamRegistryRequest,
|
|
typeField: string,
|
|
textField: string,
|
|
secretField: string,
|
|
secretSpacePathField: string,
|
|
scope?: Scope
|
|
) {
|
|
const accessKeyType = get(formData, typeField)
|
|
if (accessKeyType === SecretValueType.TEXT) {
|
|
const value = get(formData, textField, '')
|
|
set(formData, textField, value)
|
|
set(formData, typeField, undefined)
|
|
set(formData, secretField, undefined)
|
|
set(formData, secretSpacePathField, undefined)
|
|
} else {
|
|
const accessKeySecret = get(formData, secretField)
|
|
set(formData, secretSpacePathField, getSecretSpacePath(get(accessKeySecret, 'referenceString', ''), scope))
|
|
set(formData, secretField, get(accessKeySecret, 'identifier'))
|
|
set(formData, typeField, undefined)
|
|
set(formData, textField, undefined)
|
|
}
|
|
}
|
|
|
|
export function getFormattedFormDataForAuthType(
|
|
values: UpstreamRegistryRequest,
|
|
parent?: Parent,
|
|
scope?: Scope
|
|
): UpstreamRegistryRequest {
|
|
return produce(values, (draft: UpstreamRegistryRequest) => {
|
|
if (draft.config.authType === UpstreamProxyAuthenticationMode.USER_NAME_AND_PASSWORD) {
|
|
set(draft, 'config.auth.authType', draft.config.authType)
|
|
if (parent === Parent.Enterprise) {
|
|
convertSecretInputToFormFields(draft, 'config.auth.secretIdentifier', 'config.auth.secretSpacePath', scope)
|
|
}
|
|
} else if (draft.config.authType === UpstreamProxyAuthenticationMode.ACCESS_KEY_AND_SECRET_KEY) {
|
|
set(draft, 'config.auth.authType', draft.config.authType)
|
|
if (parent === Parent.Enterprise) {
|
|
convertSecretInputToFormFields(
|
|
draft,
|
|
'config.auth.secretKeyIdentifier',
|
|
'config.auth.secretKeySpacePath',
|
|
scope
|
|
)
|
|
convertMultiTypeSecretInputToFormFields(
|
|
draft,
|
|
'config.auth.accessKeyType',
|
|
'config.auth.accessKey',
|
|
'config.auth.accessKeySecretIdentifier',
|
|
'config.auth.accessKeySecretSpacePath',
|
|
scope
|
|
)
|
|
}
|
|
} else if (draft.config.authType === UpstreamProxyAuthenticationMode.ANONYMOUS) {
|
|
set(draft, 'config.auth', null)
|
|
}
|
|
if (draft.config.source === UpstreamRepositoryURLInputSource.Dockerhub) {
|
|
set(draft, 'config.url', '')
|
|
}
|
|
})
|
|
}
|
|
|
|
export function getSecretScopeDetailsByIdentifier(identifier: string, secretSpacePath: string) {
|
|
const referenceString = getReferenceStringFromSecretSpacePath(identifier, secretSpacePath)
|
|
const [, orgIdentifier, projectIdentifier] = secretSpacePath.split('/')
|
|
return {
|
|
identifier: identifier,
|
|
name: identifier,
|
|
referenceString,
|
|
orgIdentifier,
|
|
projectIdentifier
|
|
}
|
|
}
|
|
|
|
function convertFormFieldsToSecreteInput(
|
|
formData: UpstreamRegistryRequest,
|
|
secretField: string,
|
|
secretSpacePathField: string
|
|
) {
|
|
const secretIdentifier = get(formData, secretField, '')
|
|
const secretSpacePath = get(formData, secretSpacePathField, '')
|
|
set(formData, secretField, getSecretScopeDetailsByIdentifier(secretIdentifier, secretSpacePath))
|
|
}
|
|
|
|
function convertFormFieldsToMultiTypeSecretInput(
|
|
formData: UpstreamRegistryRequest,
|
|
typeField: string,
|
|
formField: string,
|
|
secretIdentifierField: string,
|
|
secretSpacePathField: string
|
|
) {
|
|
const value = get(formData, formField)
|
|
if (value) {
|
|
set(formData, formField, value)
|
|
set(formData, typeField, SecretValueType.TEXT)
|
|
set(formData, secretIdentifierField, undefined)
|
|
set(formData, secretSpacePathField, undefined)
|
|
} else {
|
|
const secretIdentifierValue = get(formData, secretIdentifierField, '')
|
|
const secretSpacePathValue = get(formData, secretSpacePathField, '')
|
|
set(formData, typeField, SecretValueType.ENCRYPTED)
|
|
set(formData, secretIdentifierField, getSecretScopeDetailsByIdentifier(secretIdentifierValue, secretSpacePathValue))
|
|
set(formData, formField, undefined)
|
|
}
|
|
}
|
|
|
|
export function getFormattedInitialValuesForAuthType(values: UpstreamRegistryRequest, parent?: Parent) {
|
|
return produce(values, (draft: UpstreamRegistryRequest) => {
|
|
if (draft.config.authType === UpstreamProxyAuthenticationMode.USER_NAME_AND_PASSWORD) {
|
|
if (parent === Parent.Enterprise) {
|
|
convertFormFieldsToSecreteInput(draft, 'config.auth.secretIdentifier', 'config.auth.secretSpacePath')
|
|
}
|
|
}
|
|
if (draft.config.authType === UpstreamProxyAuthenticationMode.ACCESS_KEY_AND_SECRET_KEY) {
|
|
if (parent === Parent.Enterprise) {
|
|
convertFormFieldsToSecreteInput(draft, 'config.auth.secretKeyIdentifier', 'config.auth.secretKeySpacePath')
|
|
convertFormFieldsToMultiTypeSecretInput(
|
|
draft,
|
|
'config.auth.accessKeyType',
|
|
'config.auth.accessKey',
|
|
'config.auth.accessKeySecretIdentifier',
|
|
'config.auth.accessKeySecretSpacePath'
|
|
)
|
|
} else {
|
|
const accessKeyType = isEmpty(get(draft, 'config.auth.accessKey'))
|
|
? SecretValueType.ENCRYPTED
|
|
: SecretValueType.TEXT
|
|
set(draft, 'config.auth.accessKeyType', accessKeyType)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
export function getValidationSchemaForUpstreamForm(getString: (key: StringKeys, vars?: Record<string, any>) => string) {
|
|
return {
|
|
config: Yup.object().shape({
|
|
authType: Yup.string()
|
|
.required()
|
|
.oneOf([
|
|
UpstreamProxyAuthenticationMode.ANONYMOUS,
|
|
UpstreamProxyAuthenticationMode.USER_NAME_AND_PASSWORD,
|
|
UpstreamProxyAuthenticationMode.ACCESS_KEY_AND_SECRET_KEY
|
|
]),
|
|
auth: Yup.object()
|
|
.when(['authType'], {
|
|
is: (authType: UpstreamProxyAuthenticationMode) =>
|
|
authType === UpstreamProxyAuthenticationMode.USER_NAME_AND_PASSWORD,
|
|
then: (schema: Yup.ObjectSchema<UserPassword | Anonymous>) =>
|
|
schema.shape({
|
|
userName: Yup.string().trim().required(getString('validationMessages.userNameRequired')),
|
|
secretIdentifier: Yup.string().trim().required(getString('validationMessages.passwordRequired'))
|
|
}),
|
|
otherwise: Yup.object().optional().nullable()
|
|
})
|
|
.when(['authType'], {
|
|
is: (authType: UpstreamProxyAuthenticationMode) =>
|
|
authType === UpstreamProxyAuthenticationMode.ACCESS_KEY_AND_SECRET_KEY,
|
|
then: (schema: Yup.ObjectSchema<AccessKeySecretKey | Anonymous>) =>
|
|
schema.shape({
|
|
accessKey: Yup.string()
|
|
.trim()
|
|
.test('access-key-validation', getString('validationMessages.accessKeyRequired'), function (value) {
|
|
if (this.parent.accessKeyType === SecretValueType.TEXT) {
|
|
return !isEmpty(value)
|
|
}
|
|
return true
|
|
}),
|
|
accessKeySecretIdentifier: Yup.string()
|
|
.trim()
|
|
.test(
|
|
'access-key-secret-validation',
|
|
getString('validationMessages.accessKeyRequired'),
|
|
function (value) {
|
|
if (this.parent.accessKeyType === SecretValueType.ENCRYPTED) {
|
|
return !isEmpty(value)
|
|
}
|
|
return true
|
|
}
|
|
),
|
|
secretKeyIdentifier: Yup.string().trim().required(getString('validationMessages.secretKeyRequired'))
|
|
}),
|
|
otherwise: Yup.object().optional().nullable()
|
|
})
|
|
.nullable(),
|
|
url: Yup.string().when(['source'], {
|
|
is: (source: UpstreamRepositoryURLInputSource) =>
|
|
[UpstreamRepositoryURLInputSource.Custom, UpstreamRepositoryURLInputSource.AwsEcr].includes(source),
|
|
then: (schema: Yup.StringSchema) =>
|
|
schema
|
|
.trim()
|
|
.required(getString('validationMessages.urlRequired'))
|
|
.matches(URL_REGEX, getString('validationMessages.urlPattern')),
|
|
otherwise: (schema: Yup.StringSchema) => schema.trim().notRequired()
|
|
})
|
|
}),
|
|
cleanupPolicy: Yup.array()
|
|
.of(
|
|
Yup.object().shape({
|
|
name: Yup.string().trim().required(getString('validationMessages.cleanupPolicy.nameRequired')),
|
|
expireDays: Yup.number()
|
|
.required(getString('validationMessages.cleanupPolicy.expireDaysRequired'))
|
|
.positive(getString('validationMessages.cleanupPolicy.positiveExpireDays'))
|
|
})
|
|
)
|
|
.optional()
|
|
.nullable()
|
|
}
|
|
}
|