Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions docs/commands/rhoas_kafka_acl.adoc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/commands/rhoas_kafka_acl_grant-access.adoc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions docs/commands/rhoas_kafka_acl_grant-admin.adoc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/cmd/kafka/acl/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package acl

import (
"github.com/redhat-developer/app-services-cli/pkg/cmd/factory"
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/admin"
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/delete"
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/grant"
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/list"
Expand All @@ -22,6 +23,7 @@ func NewAclCommand(f *factory.Factory) *cobra.Command {
list.NewListACLCommand(f),
grant.NewGrantPermissionsACLCommand(f),
delete.NewDeleteCommand(f),
admin.NewAdminACLCommand(f),
)

return cmd
Expand Down
175 changes: 175 additions & 0 deletions pkg/cmd/kafka/acl/admin/admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package admin

import (
"context"

"github.com/AlecAivazis/survey/v2"
"github.com/redhat-developer/app-services-cli/internal/config"
"github.com/redhat-developer/app-services-cli/pkg/cmd/factory"
"github.com/redhat-developer/app-services-cli/pkg/connection"
"github.com/redhat-developer/app-services-cli/pkg/dump"
"github.com/redhat-developer/app-services-cli/pkg/icon"
"github.com/redhat-developer/app-services-cli/pkg/iostreams"
"github.com/redhat-developer/app-services-cli/pkg/kafka/aclutil"
"github.com/redhat-developer/app-services-cli/pkg/localize"
"github.com/redhat-developer/app-services-cli/pkg/logging"
"github.com/spf13/cobra"

flagset "github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/flagutil"

kafkainstanceclient "github.com/redhat-developer/app-services-sdk-go/kafkainstance/apiv1internal/client"
)

var (
serviceAccount string
userID string
allAccounts bool
)

type options struct {
config config.IConfig
connection factory.ConnectionFunc
logger logging.Logger
io *iostreams.IOStreams
localizer localize.Localizer
context context.Context

kafkaID string
principal string
skipConfirm bool
}

// NewAdminACLCommand creates ACL rule to aloow user to add and delete ACL rules
func NewAdminACLCommand(f *factory.Factory) *cobra.Command {

opts := &options{
config: f.Config,
connection: f.Connection,
logger: f.Logger,
io: f.IOStreams,
localizer: f.Localizer,
context: f.Context,
}

cmd := &cobra.Command{
Use: "grant-admin",
Short: f.Localizer.MustLocalize("kafka.acl.grantAdmin.cmd.shortDescription"),
Long: f.Localizer.MustLocalize("kafka.acl.grantAdmin.cmd.longDescription"),
Example: f.Localizer.MustLocalize("kafka.acl.grantAdmin.cmd.example"),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {

cfg, err := opts.config.Load()
if err != nil {
return err
}

instanceID, ok := cfg.GetKafkaIdOk()

if !ok {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.noKafkaSelected")
}

opts.kafkaID = instanceID

// check if principal is provided
if userID == "" && serviceAccount == "" && !allAccounts {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.noPrincipalsSelected")
}

// user and service account can't be along with "--all-accounts" flag
if allAccounts && (serviceAccount != "" || userID != "") {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.allAccountsCannotBeUsedWithUserFlag")
}

// user and service account should not allow wildcard
if userID == aclutil.Wildcard || serviceAccount == aclutil.Wildcard {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.useAllAccountsFlag")
}

if userID != "" {
opts.principal = userID
}

if serviceAccount != "" {
opts.principal = serviceAccount
}

if allAccounts {
opts.principal = aclutil.Wildcard
}

return runAdmin(opts)
},
}

fs := flagset.NewFlagSet(cmd, opts.localizer, opts.connection)

fs.AddUser(&userID)
fs.AddServiceAccount(&serviceAccount)
fs.AddAllAccounts(&allAccounts)
fs.AddYes(&opts.skipConfirm)

return cmd
}

func runAdmin(opts *options) (err error) {

conn, err := opts.connection(connection.DefaultConfigRequireMasAuth)
if err != nil {
return err
}

api, kafkaInstance, err := conn.API().KafkaAdmin(opts.kafkaID)
if err != nil {
return err
}

kafkaName := kafkaInstance.GetName()

req := api.AclsApi.CreateAcl(opts.context)

aclBindClusterAlter := kafkainstanceclient.NewAclBinding(
kafkainstanceclient.ACLRESOURCETYPE_CLUSTER,
aclutil.KafkaCluster,
kafkainstanceclient.ACLPATTERNTYPE_LITERAL,
aclutil.FormatPrincipal(opts.principal),
kafkainstanceclient.ACLOPERATION_ALTER,
kafkainstanceclient.ACLPERMISSIONTYPE_ALLOW,
)

opts.logger.Info(opts.localizer.MustLocalize("kafka.acl.grantPermissions.log.info.aclsPreview"))
opts.logger.Info()

rows := aclutil.MapACLsToTableRows([]kafkainstanceclient.AclBinding{*aclBindClusterAlter}, opts.localizer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably skip that when -y is passed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep -y for the confirmation action only and display the ACLs to be generated in both scenario.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. I think it needs an explanation to give context to the table though because right now it prints without info about it.

❯ ./rhoas kafka acl grant-admin --all-accounts -y
  PRINCIPAL      PERMISSION   OPERATION   DESCRIPTION                 
 -------------- ------------ ----------- ---------------------------- 
  All accounts   ALLOW        ALTER       CLUSTER is "kafka-cluster"  

✔️ Account "*" is now allowed to create and delete ACLs for Kafka instance "enda-dev"

It might be nice to add something like:

The following ACLs will be created:

...

above it.

dump.Table(opts.io.Out, rows)
opts.logger.Info()

if !opts.skipConfirm {
var confirmGrant bool
promptConfirmGrant := &survey.Confirm{
Message: opts.localizer.MustLocalize("kafka.acl.common.input.confirmGrant.message"),
}

err = survey.AskOne(promptConfirmGrant, &confirmGrant)
if err != nil {
return err
}

if !confirmGrant {
opts.logger.Debug(opts.localizer.MustLocalize("kafka.acl.grantAdmin.log.debug.grantNotConfirmed"))
return nil
}
}

req = req.AclBinding(*aclBindClusterAlter)

err = aclutil.ExecuteACLRuleCreate(req, opts.localizer, kafkaName)
if err != nil {
return err
}

opts.logger.Info(icon.SuccessPrefix(), opts.localizer.MustLocalize("kafka.acl.grantAdmin.log.info.successful", localize.NewEntry("Account", opts.principal), localize.NewEntry("InstanceName", kafkaName)))

return nil
}
2 changes: 1 addition & 1 deletion pkg/cmd/kafka/acl/delete/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func validateAndSetOpts(opts *options) error {
opts.principal = aclutil.Wildcard
}

// check if priincipal is provided
// check if principal is provided
if !allAccounts && (userID == "" && serviceAccount == "") {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.noPrincipalsSelected")
}
Expand Down
19 changes: 12 additions & 7 deletions pkg/cmd/kafka/acl/grant/grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,18 +270,18 @@ func runGrantPermissions(opts *options) (err error) {
opts.Logger.Info()

if !opts.force {
var confirmDelete bool
promptConfirmDelete := &survey.Confirm{
Message: opts.localizer.MustLocalize("kafka.acl.grantPermissions.input.confirmGrant.message"),
var confirmGrant bool
promptConfirmGrant := &survey.Confirm{
Message: opts.localizer.MustLocalize("kafka.acl.common.input.confirmGrant.message"),
}

err = survey.AskOne(promptConfirmDelete, &confirmDelete)
err = survey.AskOne(promptConfirmGrant, &confirmGrant)
if err != nil {
return err
}

if !confirmDelete {
opts.Logger.Debug(opts.localizer.MustLocalize("kafka.acl.grantPermissions.log.debug.deleteNotConfirmed"))
if !confirmGrant {
opts.Logger.Debug(opts.localizer.MustLocalize("kafka.acl.grantPermissions.log.debug.grantNotConfirmed"))
return nil
}
}
Expand All @@ -306,7 +306,7 @@ func validateFlagInputCombination(opts *options) error {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.noOperationSpecified")
}

// check if priincipal is provided
// check if principal is provided
if userID == "" && serviceAccount == "" && !allAccounts {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.noPrincipalsSelected")
}
Expand Down Expand Up @@ -350,5 +350,10 @@ func validateFlagInputCombination(opts *options) error {
)
}

// user and service account should not allow wildcard
if userID == aclutil.Wildcard || serviceAccount == aclutil.Wildcard {
return opts.localizer.MustLocalizeError("kafka.acl.common.error.useAllAccountsFlag")
}

return nil
}
Loading