UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

581 lines (549 loc) 23 kB
import { AppConstants } from '../constants/app.constants.js'; import yaml from 'js-yaml'; import { UpperCaseKinds, YamlContent, Api_Spec_Ref, Policy_Spec_Ref, PolicySequence_Spec_Ref, Scope_Spec_Ref, Transport_Spec_Ref, Routing_Spec_Ref, SetContextVariable_Spec_Ref, IAM_Spec_Ref, OutboundAlias_Spec_Ref, OutboundBasicAuth_Spec_Ref, OutboundKerberosAuth_Spec_Ref, OutboundNTLMAuth_Spec_Ref, OutboundOAuth2_Spec_Ref, DataMasking_Spec_Ref, WebMethodsISService_Spec_Ref, AuthorizeUser_Spec_Ref, InboundBulkHead_Spec_Ref, InboundMessaging_Spec_Ref, MessageConfig_Spec_Ref, Tuple_Spec_Ref, CustomHttpHeader_Spec_Ref, SetMediaType_Spec_Ref } from '../model/interface.js'; import { KindEnums } from '@apic/api-model/common/StudioEnums.js'; import { LogWrapper } from './log-wrapper.js'; import { Package_Spec } from '@apic/api-model/package/Package.js'; import { Plan_Spec_Ref, RequestLimit_Spec_Ref } from '@apic/studio-client-model'; /** * AssetValidator class is responsible to check the basic structure validations for the asset yamls of studio. */ export class AssetValidator { public validateAssets(genObj: YamlContent): boolean { const kind = genObj.kind.toUpperCase(); if (!UpperCaseKinds.includes(kind)) { LogWrapper.logError('0255', `Unidentified kind: ${genObj.kind}`); return false; } LogWrapper.logInfo('0256', `${genObj.kind}`); switch (kind) { case KindEnums.API.toUpperCase(): return this.validateApi(genObj); case KindEnums.PolicySequence.toUpperCase(): return this.validatePolicySequence(genObj); case KindEnums.GlobalPolicy.toUpperCase(): return this.validateGlobalPolicy(genObj); case KindEnums.Scope.toUpperCase(): return this.validateScope(genObj); case KindEnums.Transport.toUpperCase(): return this.validateTransport(genObj); case KindEnums.Route.toUpperCase(): return this.validateRoute(genObj); case KindEnums.SetContextVariable.toUpperCase(): return this.validateSetContextVariable(genObj); case KindEnums.IdentifyAndAuthorize.toUpperCase(): return this.validateIdentifyAndAuthorize(genObj); case KindEnums.DataMasking.toUpperCase(): return this.validateDataMasking(genObj); case KindEnums.WebMethodsISService.toUpperCase(): return this.validateWebMethodsISService(genObj); case KindEnums.OutboundAlias.toUpperCase(): return this.validateOutboundAlias(genObj); case KindEnums.OutboundAnonymous.toUpperCase(): return true; case KindEnums.OutboundBasicAuth.toUpperCase(): return this.validateOutboundBasicAuth(genObj); case KindEnums.OutboundIncomingJWT.toUpperCase(): return true; case KindEnums.OutboundKerberosAuth.toUpperCase(): return this.validateOutboundKerberosAuth(genObj); case KindEnums.OutboundNTLMAuth.toUpperCase(): return this.validateOutboundNTLMAuth(genObj); case KindEnums.OutboundOAuth2.toUpperCase(): return this.validateOutboundOAuth2(genObj); case KindEnums.AuthorizeUser.toUpperCase(): return this.validateAuthorizeUser(genObj); case KindEnums.InboundBulkHead.toUpperCase(): return this.validateInboundBulkHead(genObj); case KindEnums.InboundMessaging.toUpperCase(): return this.validateInboundMessaging(genObj); case KindEnums.MessageConfig.toUpperCase(): return this.validateMessageConfig(genObj); case KindEnums.Tuple.toUpperCase(): return this.validateTuple(genObj); case KindEnums.CustomHttpHeader.toUpperCase(): return this.validateCustomHttpHeader(genObj); case KindEnums.SetMediaType.toUpperCase(): return this.validateSetMediaType(genObj); case KindEnums.Plan.toUpperCase(): return this.validatePlan(genObj); case KindEnums.Package.toUpperCase(): return this.validatePackage(genObj); case KindEnums.RequestLimit.toUpperCase(): return this.validateRequestLimit(genObj); // case KindEnums.Test.toUpperCase(): // return true; // case KindEnums.HTTPEndpoint.toUpperCase(): // return true; // case KindEnums.Environment.toUpperCase(): // return true; default: LogWrapper.logWarn('0255', `Unimplemented kind: ${kind}`); return true; // This to be updated to false once all the kinds are incorporated in studio application. } } private validateApi(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const apiSpec = yaml.load(specObj) as Api_Spec_Ref; let valid = true; const policySequence = AppConstants.POLICY_SEQ as keyof typeof apiSpec; if (apiSpec?.['api-spec'] == null || apiSpec?.[policySequence] == null) { LogWrapper.logDebug('0003', 'kind API must have api specification some policies'); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateTuple(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const tupleSpec = yaml.load(specObj) as Tuple_Spec_Ref; let valid = true; if (tupleSpec?.['value'] == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validatePolicySequence(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const policySequenceSpec = yaml.load(specObj) as PolicySequence_Spec_Ref; let valid = true; // if (policySequenceSpec?.routing == null || policySequenceSpec?.transport == null) { // LogWrapper.logError('0257', `${genObj.kind}`); // valid = false; // } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateGlobalPolicy(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const policySpec = yaml.load(specObj) as Policy_Spec_Ref; let valid = true; const policySequence = AppConstants.POLICY_SEQ as keyof typeof policySpec; if (policySpec?.[policySequence] == null && policySpec?.['filter-attributes'] == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateScope(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const scopeSpec = yaml.load(specObj) as Scope_Spec_Ref; let valid = true; if (scopeSpec?.resources == null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', 'Scope must have atleast one resource'); valid = false; } else { for (const resource of scopeSpec.resources) { if (resource.path == null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', 'Resource has no path defined.'); valid = false; } } } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateTransport(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const tranSpec = yaml.load(specObj) as Transport_Spec_Ref; let valid = true; if (tranSpec?.protocol == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateSetMediaType(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const tranSpec = yaml.load(specObj) as SetMediaType_Spec_Ref; let valid = true; if (tranSpec?.defaultContentType == null || tranSpec?.defaultAcceptHeader == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validatePlan(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const planSpec = yaml.load(specObj) as Plan_Spec_Ref; let valid = true; if (planSpec?.qos == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validatePackage(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const packageSpec = yaml.load(specObj) as Package_Spec; let valid = true; if (packageSpec?.apis == null || packageSpec?.plans == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateRequestLimit(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const requestLimitSpec = yaml.load(specObj) as RequestLimit_Spec_Ref; let valid = true; if (requestLimitSpec?.maxRequest == null || requestLimitSpec?.interval == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateRoute(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const routeSpec = yaml.load(specObj) as Routing_Spec_Ref; let valid = true; const invalidEndpoints = Object.keys(routeSpec ?? {}).filter(key => !AppConstants.ALLOWED_ENDPOINTS.includes(key)); if (routeSpec == null || invalidEndpoints.length > 0) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateCustomHttpHeader(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const headerSpec = yaml.load(specObj) as CustomHttpHeader_Spec_Ref; let valid = true; if (headerSpec?.property == null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', 'CustomHttpHeader must have atleast one property'); valid = false; } else { for (const prop of headerSpec.property) { if (prop.key == null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', 'Property has no key defined.'); valid = false; } if (prop.value == null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', 'Property has no value defined.'); valid = false; } } } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateSetContextVariable(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const setContextVariableSpec = yaml.load(specObj) as SetContextVariable_Spec_Ref; let valid = true; if (setContextVariableSpec.condition == null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have atleast one condition`); valid = false; } else if (setContextVariableSpec.variable !== undefined) { for (const variable of setContextVariableSpec.variable) { if (variable.name == null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} Context variable must have a name`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } } } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateIdentifyAndAuthorize(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const IAMSpec = yaml.load(specObj) as IAM_Spec_Ref; if (IAMSpec.identificationType === undefined) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have 'identificationType' specified.`); return false; } if (IAMSpec.identificationType === null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have at least one of 'or' or 'and' specified under identificationType.`); return false; } const hasOr = IAMSpec.identificationType.or; const hasAnd = IAMSpec.identificationType.and; if (hasOr !== undefined && hasAnd !== undefined) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', `Kind: ${genObj.kind} cannot have both 'or' and 'and' specified under identificationType.`); return false; } if (hasOr !== undefined) { if (hasOr === null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', `Kind: ${genObj.kind} 'or' under identificationType must have at least one key-value pair.`); return false; } } if (hasAnd !== undefined) { if (hasAnd === null) { LogWrapper.logError('0257', `${genObj.kind}`); LogWrapper.logDebug('0003', `Kind: ${genObj.kind} 'and' under identificationType must have at least one key-value pair.`); return false; } } LogWrapper.logInfo('0258', `${genObj.kind}`); return true; } private validateDataMasking(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const DataMaskingSpec = yaml.load(specObj) as DataMasking_Spec_Ref; let valid = true; if (DataMaskingSpec.transformations == null) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateWebMethodsISService(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const WebMethodsISServiceSpec = yaml.load(specObj) as WebMethodsISService_Spec_Ref; let valid = true; if (WebMethodsISServiceSpec.services !== undefined) { for (const service of WebMethodsISServiceSpec.services) { if (service.name == null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} Service must have a name`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } } } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateOutboundAlias(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const OutboundAliasSpec = yaml.load(specObj) as OutboundAlias_Spec_Ref; let valid = true; return valid; } private validateOutboundBasicAuth(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const OutboundBasicAuthSpec = yaml.load(specObj) as OutboundBasicAuth_Spec_Ref; let valid = true; if (OutboundBasicAuthSpec.use_incoming_credentials === undefined && OutboundBasicAuthSpec.use_custom_credentials === undefined) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have useIncomingCredentials or useCustomCredentials`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } else if (OutboundBasicAuthSpec.use_custom_credentials !== null) { if (OutboundBasicAuthSpec.use_custom_credentials?.username === null || OutboundBasicAuthSpec.use_custom_credentials?.password === null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} useCustomCredentials must have username and password`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } } return valid; } private validateOutboundKerberosAuth(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const OutboundKerberosAuthSpec = yaml.load(specObj) as OutboundKerberosAuth_Spec_Ref; let valid = true; if (OutboundKerberosAuthSpec.use_incoming_http_credentials !== undefined) { valid = valid && this.validateKerberosUseIncomingHttpCredentials(OutboundKerberosAuthSpec, genObj); } else if (OutboundKerberosAuthSpec.use_delegate_incoming_credentials !== undefined) { valid = valid && this.validateKerberosUseDelegateIncomingCredentials(OutboundKerberosAuthSpec, genObj); } else if (OutboundKerberosAuthSpec.use_custom_credentials !== undefined) { valid = valid && this.validateKerberosUseCustomCredentials(OutboundKerberosAuthSpec, genObj); } return valid; } getNoOfKerberosAuthMethods(OutboundKerberosAuthSpec: OutboundKerberosAuth_Spec_Ref): number { let count = 0; if (OutboundKerberosAuthSpec.use_incoming_http_credentials !== undefined) { count += 1; } if (OutboundKerberosAuthSpec.use_custom_credentials !== undefined) { count += 1; } if (OutboundKerberosAuthSpec.use_delegate_incoming_credentials !== undefined) { count += 1; } if (OutboundKerberosAuthSpec.use_incoming_kerbose_credentials !== undefined) { count += 1; } return count; } validateKerberosUseCustomCredentials(OutboundKerberosAuthSpec: OutboundKerberosAuth_Spec_Ref, genObj: YamlContent): boolean { if (OutboundKerberosAuthSpec.use_custom_credentials.client_principal === null || OutboundKerberosAuthSpec.use_custom_credentials.client_password === null || OutboundKerberosAuthSpec.use_custom_credentials.service_principal === null || OutboundKerberosAuthSpec.use_custom_credentials.service_principal_nameform === null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} useCustomCredentials must have clientPrincipal, clientPassword servicePrincipal and servicePrincipalNameFrom`); LogWrapper.logError('0257', `${genObj.kind}`); return false; } return true; } validateKerberosUseDelegateIncomingCredentials(OutboundKerberosAuthSpec: OutboundKerberosAuth_Spec_Ref, genObj: YamlContent): boolean { if (OutboundKerberosAuthSpec.use_delegate_incoming_credentials.client_principal === null || OutboundKerberosAuthSpec.use_delegate_incoming_credentials.client_password === null || OutboundKerberosAuthSpec.use_delegate_incoming_credentials.service_principal === null || OutboundKerberosAuthSpec.use_delegate_incoming_credentials.service_principal_nameform === null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} useDelegateIncomingCredentials must have clientPrincipal, clientPassword servicePrincipal and servicePrincipalNameFrom`); LogWrapper.logError('0257', `${genObj.kind}`); return false; } return true; } validateKerberosUseIncomingHttpCredentials(OutboundKerberosAuthSpec: OutboundKerberosAuth_Spec_Ref, genObj: YamlContent) { if (OutboundKerberosAuthSpec.use_incoming_http_credentials.service_principal === null || OutboundKerberosAuthSpec.use_incoming_http_credentials.service_principal_nameform === null ) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} useIncomingHttpCredentials must have servicePrincipal and servicePrincipalNameFrom`); LogWrapper.logError('0257', `${genObj.kind}`); return false; } return true; } private validateOutboundNTLMAuth(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const OutboundNTLMAuthSpec = yaml.load(specObj) as OutboundNTLMAuth_Spec_Ref; let valid = true; if (OutboundNTLMAuthSpec.use_custom_credentials === undefined && OutboundNTLMAuthSpec.use_incoming_http_credentials === undefined && OutboundNTLMAuthSpec.use_transparent_auth === undefined) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have useIncomingHttpCredentials or useCustomCredentials or useTransparentAuth`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } return valid; } private validateOutboundOAuth2(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const OutboundOAuth2Spec = yaml.load(specObj) as OutboundOAuth2_Spec_Ref; let valid = true; if (OutboundOAuth2Spec.use_incoming_oauth_token === undefined && OutboundOAuth2Spec.use_custom_credentials === undefined) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have useIncomingOauthToken or useCustomCredentials`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } else if (OutboundOAuth2Spec.use_custom_credentials !== null) { if (OutboundOAuth2Spec.use_custom_credentials?.token === null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} useCustomCredentials must have OAuth Token defined`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } } return valid; } private validateAuthorizeUser(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const AuthorizeUserSpec = yaml.load(specObj) as AuthorizeUser_Spec_Ref; let valid = true; if (AuthorizeUserSpec.users === undefined && AuthorizeUserSpec.teams === undefined && AuthorizeUserSpec.groups === undefined) { LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateInboundBulkHead(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const InboundBulkHeadSpec = yaml.load(specObj) as InboundBulkHead_Spec_Ref; let valid = true; if (InboundBulkHeadSpec.maxConcurrentCalls == null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have maxConcurrentCalls defined`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } if (InboundBulkHeadSpec.enableBulkheadForCallbacks !== null) { if (InboundBulkHeadSpec.enableBulkheadForCallbacks?.maxConcurrentCallbacks === null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} enableBulkheadForCallbacks must have maxConcurrentCallbacks defined`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } } if (InboundBulkHeadSpec.retryAfterResponseHeader !== null) { if (InboundBulkHeadSpec.retryAfterResponseHeader?.retryAfterValue === null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} retryAfterResponseHeader must have retryAfterValue defined`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateInboundMessaging(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const InboundMessagingSpec = yaml.load(specObj) as InboundMessaging_Spec_Ref; let valid = true; if (InboundMessagingSpec.alias === undefined) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have connection alias defined`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } if (InboundMessagingSpec.source === null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have source defined`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } else { for (const source of InboundMessagingSpec.source) { if (source.name == null || source.resource == null || source.type == null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} Source must have a name, type and resource`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } } } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } private validateMessageConfig(genObj: YamlContent): boolean { const specObj = JSON.stringify(genObj.spec); const MessageConfigSpec = yaml.load(specObj) as MessageConfig_Spec_Ref; let valid = true; if (MessageConfigSpec.connectionAlias == null || MessageConfigSpec.destination.name == null || MessageConfigSpec.destination.type == null || MessageConfigSpec.deliveryMode == null || MessageConfigSpec.replyTo.type == null || MessageConfigSpec.ttl == null || MessageConfigSpec.timeToWait == null) { LogWrapper.logDebug('0003', `Kind: ${genObj.kind} must have connection Alias, destination name, destination type, replyTo type, ttl, timeToWait and deliveryMode`); LogWrapper.logError('0257', `${genObj.kind}`); valid = false; } LogWrapper.logInfo('0258', `${genObj.kind}`); return valid; } }