Skip to content

Commit 34c6edf

Browse files
committed
OCM-18900 | fix: Improve error message for proxy passwords with special characters
1 parent 0d193e3 commit 34c6edf

File tree

8 files changed

+353
-57
lines changed

8 files changed

+353
-57
lines changed

cmd/create/cluster/cmd.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3053,15 +3053,15 @@ func run(cmd *cobra.Command, _ []string) {
30533053
Help: cmd.Flags().Lookup("https-proxy").Usage,
30543054
Default: httpsProxy,
30553055
Validators: []interactive.Validator{
3056-
interactive.IsURL,
3056+
ocm.ValidateHTTPSProxy,
30573057
},
30583058
})
30593059
if err != nil {
30603060
r.Reporter.Errorf("Expected a valid https proxy: %s", err)
30613061
os.Exit(1)
30623062
}
30633063
}
3064-
err = interactive.IsURL(httpsProxy)
3064+
err = ocm.ValidateHTTPSProxy(httpsProxy)
30653065
if err != nil {
30663066
r.Reporter.Errorf("%s", err)
30673067
os.Exit(1)

cmd/edit/cluster/cmd.go

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ func run(cmd *cobra.Command, _ []string) {
513513
}
514514
}
515515
if httpsProxy != nil && *httpsProxy != input.DoubleQuotesToRemove {
516-
err = interactive.IsURL(*httpsProxy)
516+
err = ocm.ValidateHTTPSProxy(*httpsProxy)
517517
if err != nil {
518518
r.Reporter.Errorf("%s", err)
519519
os.Exit(1)
@@ -562,10 +562,7 @@ func run(cmd *cobra.Command, _ []string) {
562562
}
563563

564564
/******* AdditionalTrustBundle *******/
565-
updateAdditionalTrustBundle := false
566-
if additionalTrustBundleFile != nil {
567-
updateAdditionalTrustBundle = true
568-
}
565+
updateAdditionalTrustBundle := additionalTrustBundleFile != nil
569566
if useExistingVPC && !updateAdditionalTrustBundle && additionalTrustBundleFile == nil &&
570567
interactive.Enabled() {
571568
updateAdditionalTrustBundleValue, err := interactive.GetBool(interactive.Input{
@@ -620,10 +617,7 @@ func run(cmd *cobra.Command, _ []string) {
620617
}
621618

622619
/******* AdditionalAllowedPrincipals *******/
623-
updateAdditionalAllowedPrincipals := false
624-
if additionalAllowedPrincipals != nil {
625-
updateAdditionalAllowedPrincipals = true
626-
}
620+
updateAdditionalAllowedPrincipals := additionalAllowedPrincipals != nil
627621
if !updateAdditionalAllowedPrincipals && additionalAllowedPrincipals == nil &&
628622
interactive.Enabled() {
629623
updateAdditionalAllowedPrincipalsValue, err := interactive.GetBool(interactive.Input{
@@ -1013,15 +1007,15 @@ func warnUserForOAuthHCPVisibility(r *rosa.Runtime, clusterKey string, cluster *
10131007
func validateExpiration() (expiration time.Time, err error) {
10141008
// Validate options
10151009
if len(args.expirationTime) > 0 && args.expirationDuration != 0 {
1016-
err = errors.New("At most one of 'expiration-time' or 'expiration' may be specified")
1010+
err = errors.New("at most one of 'expiration-time' or 'expiration' may be specified")
10171011
return
10181012
}
10191013

10201014
// Parse the expiration options
10211015
if len(args.expirationTime) > 0 {
10221016
t, err := parseRFC3339(args.expirationTime)
10231017
if err != nil {
1024-
err = fmt.Errorf("Failed to parse expiration-time: %s", err)
1018+
err = fmt.Errorf("failed to parse expiration-time: %s", err)
10251019
return expiration, err
10261020
}
10271021

@@ -1039,7 +1033,7 @@ func validateExpiration() (expiration time.Time, err error) {
10391033
func validateOvnInternalSubnetConfiguration() (ovnInternalSubnets map[string]string, err error) {
10401034
if len(args.ovnInternalSubnets) > 0 {
10411035
if args.networkType == "" {
1042-
err = fmt.Errorf("Expected a value for %s when supplying the flag %s", ocm.NetworkTypeFlagName,
1036+
err = fmt.Errorf("expected a value for %s when supplying the flag %s", ocm.NetworkTypeFlagName,
10431037
ocm.OvnInternalSubnetsFlagName)
10441038
return
10451039
}
@@ -1052,7 +1046,7 @@ func validateOvnInternalSubnetConfiguration() (ovnInternalSubnets map[string]str
10521046
func validateNetworkType() (networkConfig string, err error) {
10531047
if len(args.networkType) > 0 {
10541048
if args.networkType != ocm.NetworkTypeOvn && args.networkType != ocm.NetworkTypeOvnAlias {
1055-
err = fmt.Errorf("Incorrect network type '%s', please use '%s' or remove the flag",
1049+
err = fmt.Errorf("incorrect network type '%s', please use '%s' or remove the flag",
10561050
args.networkType, ocm.NetworkTypeOvn)
10571051
} else {
10581052
networkConfig = ocm.NetworkTypeOvn // allows use of alias (OVN-Kubernetes)- but sets it to correct value for API
@@ -1088,11 +1082,11 @@ func setAuditLogForwarding(r *rosa.Runtime, cmd *cobra.Command, cluster *cmv1.Cl
10881082
argValuePtr *string, err error) {
10891083
if cmd.Flags().Changed("audit-log-arn") {
10901084
if !aws.IsHostedCP(cluster) {
1091-
return nil, fmt.Errorf("Audit log forwarding to AWS CloudWatch is only supported for Hosted Control Plane clusters")
1085+
return nil, fmt.Errorf("audit log forwarding to AWS CloudWatch is only supported for Hosted Control Plane clusters")
10921086

10931087
}
10941088
if auditLogArn != "" && !aws.RoleArnRE.MatchString(auditLogArn) {
1095-
return nil, fmt.Errorf("Expected a valid value for audit-log-arn matching %s", aws.RoleArnRE.String())
1089+
return nil, fmt.Errorf("expected a valid value for audit-log-arn matching %s", aws.RoleArnRE.String())
10961090
}
10971091
argValuePtr := new(string)
10981092
*argValuePtr = auditLogArn
@@ -1129,7 +1123,7 @@ func auditLogInteractivePrompt(r *rosa.Runtime, cmd *cobra.Command, cluster *cmv
11291123
Required: true,
11301124
})
11311125
if err != nil {
1132-
return nil, fmt.Errorf("Expected a valid value: %s", err)
1126+
return nil, fmt.Errorf("expected a valid value: %s", err)
11331127
}
11341128
if requestAuditLogForwarding {
11351129

@@ -1146,7 +1140,7 @@ func auditLogInteractivePrompt(r *rosa.Runtime, cmd *cobra.Command, cluster *cmv
11461140
},
11471141
})
11481142
if err != nil {
1149-
return nil, fmt.Errorf("Expected a valid value for audit-log-arn: %s", err)
1143+
return nil, fmt.Errorf("expected a valid value for audit-log-arn: %s", err)
11501144
}
11511145
*auditLogRolePtr = auditLogRoleValue
11521146
return auditLogRolePtr, nil
@@ -1159,7 +1153,7 @@ func auditLogInteractivePrompt(r *rosa.Runtime, cmd *cobra.Command, cluster *cmv
11591153
Default: false,
11601154
})
11611155
if err != nil {
1162-
return nil, fmt.Errorf("Expected a valid value: %s", err)
1156+
return nil, fmt.Errorf("expected a valid value: %s", err)
11631157
}
11641158
if disableAuditLog {
11651159
*auditLogRolePtr = ""
@@ -1190,7 +1184,7 @@ func BuildClusterConfigWithRegistry(clusterConfig ocm.Spec, allowedRegistries []
11901184
ca, err := clusterregistryconfig.BuildAdditionalTrustedCAFromInputFile(additionalTrustedCa)
11911185
if err != nil {
11921186
return clusterConfig, fmt.Errorf(
1193-
"Failed to build the additional trusted ca from file %s, got error: %s",
1187+
"failed to build the additional trusted ca from file %s, got error: %s",
11941188
additionalTrustedCa, err)
11951189
}
11961190
clusterConfig.AdditionalTrustedCa = ca

cmd/edit/cluster/cmd_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ var _ = Describe("Edit cluster", func() {
108108
})
109109
It("KO: should fail with error if ca file does not exist", func() {
110110
_, err := BuildClusterConfigWithRegistry(clusterConfig, allowedRegistries, nil, nil, "not-exist", "", "")
111-
Expect(err).To(MatchError("Failed to build the additional trusted ca from file not-exist, " +
111+
Expect(err).To(MatchError("failed to build the additional trusted ca from file not-exist, " +
112112
"got error: expected a valid additional trusted certificate spec file:" +
113113
" open not-exist: no such file or directory"))
114114
})

pkg/helper/helpers.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,65 @@ func FilterEmptyStrings(strings []string) []string {
268268
}
269269
return filteredResult
270270
}
271+
272+
type URLCredentialValidation struct {
273+
Field string
274+
InvalidChar rune
275+
HasInvalid bool
276+
}
277+
278+
func ValidateURLCredentials(urlString string) *URLCredentialValidation {
279+
if !strings.Contains(urlString, "@") || !strings.Contains(urlString, "://") {
280+
return &URLCredentialValidation{HasInvalid: false}
281+
}
282+
283+
schemeIdx := strings.Index(urlString, "://")
284+
if schemeIdx == -1 {
285+
return &URLCredentialValidation{HasInvalid: false}
286+
}
287+
288+
rest := urlString[schemeIdx+3:]
289+
atIdx := strings.LastIndex(rest, "@")
290+
if atIdx == -1 {
291+
return &URLCredentialValidation{HasInvalid: false}
292+
}
293+
294+
userinfo := rest[:atIdx]
295+
296+
if strings.Count(urlString, "@") > 1 {
297+
return &URLCredentialValidation{
298+
Field: "password",
299+
InvalidChar: '@',
300+
HasInvalid: true,
301+
}
302+
}
303+
304+
colonIdx := strings.Index(userinfo, ":")
305+
if colonIdx == -1 {
306+
return checkForInvalidChars(userinfo, "username")
307+
}
308+
309+
username := userinfo[:colonIdx]
310+
if result := checkForInvalidChars(username, "username"); result.HasInvalid {
311+
return result
312+
}
313+
314+
password := userinfo[colonIdx+1:]
315+
return checkForInvalidChars(password, "password")
316+
}
317+
318+
func checkForInvalidChars(value, field string) *URLCredentialValidation {
319+
invalidChars := "/:#?[]@!$&'()*+,;="
320+
321+
for _, char := range value {
322+
if strings.ContainsRune(invalidChars, char) {
323+
return &URLCredentialValidation{
324+
Field: field,
325+
InvalidChar: char,
326+
HasInvalid: true,
327+
}
328+
}
329+
}
330+
331+
return &URLCredentialValidation{HasInvalid: false}
332+
}

pkg/interactive/validation_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ var _ = Describe("SubnetsValidator", func() {
131131

132132
err := validator(answers)
133133
Expect(err).To(HaveOccurred())
134-
Expect(err.Error()).To(ContainSubstring("The number of subnets for a public hosted " +
134+
Expect(err.Error()).To(ContainSubstring("the number of subnets for a public hosted " +
135135
"cluster should be at least two"))
136136
})
137137

@@ -153,7 +153,7 @@ var _ = Describe("SubnetsValidator", func() {
153153

154154
err := validator(answers)
155155
Expect(err).To(HaveOccurred())
156-
Expect(err.Error()).To(ContainSubstring("Must have at least one public subnet when not using both " +
156+
Expect(err.Error()).To(ContainSubstring("must have at least one public subnet when not using both " +
157157
"'private API' and 'private ingress'"))
158158
})
159159

@@ -169,7 +169,7 @@ var _ = Describe("SubnetsValidator", func() {
169169

170170
err := validator(answers)
171171
Expect(err).To(HaveOccurred())
172-
Expect(err.Error()).To(ContainSubstring("The number of subnets for a 'multi-AZ'"+
172+
Expect(err.Error()).To(ContainSubstring("the number of subnets for a 'multi-AZ'"+
173173
" 'private link cluster' should be '3', instead received: '%d'", len(answers)))
174174
})
175175

0 commit comments

Comments
 (0)