Skip to content

Commit

Permalink
fix(serviceaccount): add service account input validation
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This change restricts to possible parameters for
service account name and description, whereas before you could enter
any value.
  • Loading branch information
Enda Phelan committed Mar 29, 2021
1 parent bb1d9b4 commit f4a3e21
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 14 deletions.
2 changes: 1 addition & 1 deletion cmd/rhoas/pkged.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/commands/rhoas_kafka_topic_describe.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Describe a topic

=== Synopsis

Print detailed configuration information for a Kafka topic"
Print detailed configuration information for a Kafka topic.


....
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/nicksnyder/go-i18n/v2 v2.1.2
github.com/openconfig/goyang v0.2.4
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect
github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5
Expand Down
2 changes: 1 addition & 1 deletion locales/cmd/kafka/topic/common/active.en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ one = 'topic name is required'
one = 'topic name cannot exceed {{.MaxNameLen}} characters'

[kafka.topic.common.validation.name.error.invalidChars]
one = 'invalid topic name "{{.Name}}", only letters (Aa-Zz), numbers, "_" and "-" are accepted'
one = 'invalid topic name "{{.Name}}"; only letters (Aa-Zz), numbers, "_" and "-" are accepted'

[kafka.topic.common.validation.partitions.error.invalid]
one = 'invalid partition count {{.Partitions}}, minimum partition count is {{.MinPartitions}}'
Expand Down
15 changes: 15 additions & 0 deletions locales/cmd/serviceaccount/active.en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,18 @@ one = 'Sets a custom file location to save the credentials.'
[serviceAccount.common.log.debug.interactive.fileFormatNotSet]
description = 'debug message'
one = '--file-format flag is not set, prompting user to enter a value'

[serviceAccount.common.validation.name.error.required]
one = 'service account name is required'

[serviceAccount.common.validation.name.error.lengthError]
one = 'service account name cannot exceed {{.MaxNameLen}} characters'

[serviceAccount.common.validation.name.error.invalidChars]
one = 'invalid service account name "{{.Name}}"; only lowercase letters (a-z), numbers, and "-" are accepted'

[serviceAccount.common.validation.description.error.invalidChars]
one = 'invalid service account description "{{.Description}}"; only letters (Aa-Zz) and numbers are accepted'

[serviceAccount.common.validation.description.error.lengthError]
one = 'service account description cannot exceed {{.MaxNameLen}} characters'
31 changes: 21 additions & 10 deletions pkg/cmd/serviceaccount/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"os"

"github.com/bf2fc6cc711aee1a0c2a/cli/pkg/serviceaccount/validation"

kasclient "github.com/bf2fc6cc711aee1a0c2a/cli/pkg/api/kas/client"
"github.com/bf2fc6cc711aee1a0c2a/cli/pkg/connection"

Expand Down Expand Up @@ -50,7 +52,7 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command {
Short: localizer.MustLocalizeFromID("serviceAccount.create.cmd.shortDescription"),
Long: localizer.MustLocalizeFromID("serviceAccount.create.cmd.longDescription"),
Example: localizer.MustLocalizeFromID("serviceAccount.create.cmd.example"),
RunE: func(cmd *cobra.Command, _ []string) error {
RunE: func(cmd *cobra.Command, _ []string) (err error) {
if !opts.IO.CanPrompt() && opts.name == "" {
return fmt.Errorf(localizer.MustLocalize(&localizer.Config{
MessageID: "flag.error.requiredWhenNonInteractive",
Expand All @@ -62,13 +64,22 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command {
opts.interactive = true
}

if !opts.interactive && opts.fileFormat == "" {
return fmt.Errorf(localizer.MustLocalize(&localizer.Config{
MessageID: "flag.error.requiredFlag",
TemplateData: map[string]interface{}{
"Flag": "file-format",
},
}))
if !opts.interactive {
if opts.fileFormat == "" {
return fmt.Errorf(localizer.MustLocalize(&localizer.Config{
MessageID: "flag.error.requiredFlag",
TemplateData: map[string]interface{}{
"Flag": "file-format",
},
}))
}

if err = validation.ValidateName(opts.name); err != nil {
return err
}
if err = validation.ValidateDescription(opts.description); err != nil {
return err
}
}

// check that a valid --file-format flag value is used
Expand Down Expand Up @@ -199,7 +210,7 @@ func runInteractivePrompt(opts *Options) (err error) {
Help: localizer.MustLocalizeFromID("serviceAccount.create.input.name.help"),
}

err = survey.AskOne(promptName, &opts.name, survey.WithValidator(survey.Required))
err = survey.AskOne(promptName, &opts.name, survey.WithValidator(survey.Required), survey.WithValidator(validation.ValidateDescription))
if err != nil {
return err
}
Expand Down Expand Up @@ -228,7 +239,7 @@ func runInteractivePrompt(opts *Options) (err error) {

promptDescription := &survey.Multiline{Message: localizer.MustLocalizeFromID("serviceAccount.create.input.description.message")}

err = survey.AskOne(promptDescription, &opts.description)
err = survey.AskOne(promptDescription, &opts.description, survey.WithValidator(validation.ValidateDescription))
if err != nil {
return err
}
Expand Down
92 changes: 92 additions & 0 deletions pkg/serviceaccount/validation/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package validation

import (
"errors"
"regexp"

"github.com/bf2fc6cc711aee1a0c2a/cli/internal/localizer"
)

const (
// name validation rules
legalNameChars = "^[a-z]([-a-z0-9]*[a-z0-9])?$"
maxNameLength = 50
minNameLength = 1
// description validation rules
legalDescriptionChars = "^[a-z0-9\\s]*$"
maxDescriptionLength = 255
)

// ValidateName validates the name of the service account
func ValidateName(val interface{}) error {
name, ok := val.(string)
if !ok {
return errors.New(localizer.MustLocalize(&localizer.Config{
MessageID: "common.error.castError",
TemplateData: map[string]interface{}{
"Value": val,
"Type": "string",
},
}))
}

if len(name) < minNameLength {
return errors.New(localizer.MustLocalizeFromID("serviceAccount.common.validation.name.error.required"))
} else if len(name) > maxNameLength {
return errors.New(localizer.MustLocalize(&localizer.Config{
MessageID: "serviceAccount.common.validation.name.error.lengthError",
TemplateData: map[string]interface{}{
"MaxNameLen": maxNameLength,
},
}))
}

matched, _ := regexp.Match(legalNameChars, []byte(name))

if matched {
return nil
}

return errors.New(localizer.MustLocalize(&localizer.Config{
MessageID: "serviceAccount.common.validation.name.error.invalidChars",
TemplateData: map[string]interface{}{
"Name": name,
},
}))
}

// ValidateDescription validates the service account description text
func ValidateDescription(val interface{}) error {
description, ok := val.(string)
if !ok {
return errors.New(localizer.MustLocalize(&localizer.Config{
MessageID: "common.error.castError",
TemplateData: map[string]interface{}{
"Value": val,
"Type": "string",
},
}))
}

if len(description) > maxDescriptionLength {
return errors.New(localizer.MustLocalize(&localizer.Config{
MessageID: "serviceAccount.common.validation.description.error.lengthError",
TemplateData: map[string]interface{}{
"MaxNameLen": maxDescriptionLength,
},
}))
}

matched, _ := regexp.Match(legalDescriptionChars, []byte(description))

if matched {
return nil
}

return errors.New(localizer.MustLocalize(&localizer.Config{
MessageID: "serviceAccount.common.validation.description.error.invalidChars",
TemplateData: map[string]interface{}{
"Name": description,
},
}))
}

0 comments on commit f4a3e21

Please sign in to comment.