diff --git a/cmd/cli/app/auth/invite/invite.go b/cmd/cli/app/auth/invite/invite.go new file mode 100644 index 0000000000..63e934bdc2 --- /dev/null +++ b/cmd/cli/app/auth/invite/invite.go @@ -0,0 +1,38 @@ +// +// Copyright 2024 Stacklok, 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. + +// Package invite provides the auth invite command for the minder CLI. +package invite + +import ( + "github.com/spf13/cobra" + + "github.com/stacklok/minder/cmd/cli/app/auth" +) + +// inviteCmd represents the offline-token set of sub-commands +var inviteCmd = &cobra.Command{ + Use: "invite", + Short: "Manage user invitations", + Long: `The minder auth invite command lets you manage user invitations +for the minder control plane.`, + RunE: func(cmd *cobra.Command, _ []string) error { + return cmd.Usage() + }, +} + +func init() { + auth.AuthCmd.AddCommand(inviteCmd) +} diff --git a/cmd/cli/app/auth/invite/invite_accept.go b/cmd/cli/app/auth/invite/invite_accept.go new file mode 100644 index 0000000000..2c2b3faa11 --- /dev/null +++ b/cmd/cli/app/auth/invite/invite_accept.go @@ -0,0 +1,59 @@ +// +// Copyright 2023 Stacklok, 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. + +// Package invite provides the auth invite command for the minder CLI. +package invite + +import ( + "context" + + "github.com/spf13/cobra" + "google.golang.org/grpc" + + "github.com/stacklok/minder/internal/util/cli" + minderv1 "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1" +) + +// inviteAcceptCmd represents the accept command +var inviteAcceptCmd = &cobra.Command{ + Use: "accept", + Short: "Accept a pending invitation", + Long: `Accept a pending invitation for the current minder user`, + RunE: cli.GRPCClientWrapRunE(inviteAcceptCommand), + Args: cobra.ExactArgs(1), +} + +// inviteAcceptCommand is the whoami subcommand +func inviteAcceptCommand(ctx context.Context, cmd *cobra.Command, args []string, conn *grpc.ClientConn) error { + client := minderv1.NewUserServiceClient(conn) + code := args[0] + // No longer print usage on returned error, since we've parsed our inputs + // See https://github.com/spf13/cobra/issues/340#issuecomment-374617413 + cmd.SilenceUsage = true + + res, err := client.ResolveInvitation(ctx, &minderv1.ResolveInvitationRequest{ + Accept: true, + Code: code, + }) + if err != nil { + return cli.MessageAndError("Error resolving invitation", err) + } + cmd.Printf("Invitation %s for %s to become %s of project %s was accepted!\n", code, res.Email, res.Role, res.Project) + return nil +} + +func init() { + inviteCmd.AddCommand(inviteAcceptCmd) +} diff --git a/cmd/cli/app/auth/invite/invite_decline.go b/cmd/cli/app/auth/invite/invite_decline.go new file mode 100644 index 0000000000..564f8260ce --- /dev/null +++ b/cmd/cli/app/auth/invite/invite_decline.go @@ -0,0 +1,59 @@ +// +// Copyright 2023 Stacklok, 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. + +// Package invite provides the auth invite command for the minder CLI. +package invite + +import ( + "context" + + "github.com/spf13/cobra" + "google.golang.org/grpc" + + "github.com/stacklok/minder/internal/util/cli" + minderv1 "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1" +) + +// inviteDeclineCmd represents the decline command +var inviteDeclineCmd = &cobra.Command{ + Use: "decline", + Short: "Declines a pending invitation", + Long: `Declines a pending invitation for the current minder user`, + RunE: cli.GRPCClientWrapRunE(inviteDeclineCommand), + Args: cobra.ExactArgs(1), +} + +// inviteAcceptCommand is the whoami subcommand +func inviteDeclineCommand(ctx context.Context, cmd *cobra.Command, args []string, conn *grpc.ClientConn) error { + client := minderv1.NewUserServiceClient(conn) + code := args[0] + // No longer print usage on returned error, since we've parsed our inputs + // See https://github.com/spf13/cobra/issues/340#issuecomment-374617413 + cmd.SilenceUsage = true + + res, err := client.ResolveInvitation(ctx, &minderv1.ResolveInvitationRequest{ + Accept: false, + Code: code, + }) + if err != nil { + return cli.MessageAndError("Error resolving invitation", err) + } + cmd.Printf("Invitation %s for %s to become %s of project %s was declined!\n", code, res.Email, res.Role, res.Project) + return nil +} + +func init() { + inviteCmd.AddCommand(inviteDeclineCmd) +} diff --git a/cmd/cli/app/auth/invite/invite_list.go b/cmd/cli/app/auth/invite/invite_list.go new file mode 100644 index 0000000000..705993805f --- /dev/null +++ b/cmd/cli/app/auth/invite/invite_list.go @@ -0,0 +1,89 @@ +// +// Copyright 2023 Stacklok, 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. + +// Package invite provides the auth invite command for the minder CLI. +package invite + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "google.golang.org/grpc" + + "github.com/stacklok/minder/cmd/cli/app" + "github.com/stacklok/minder/internal/util" + "github.com/stacklok/minder/internal/util/cli" + "github.com/stacklok/minder/internal/util/cli/table" + "github.com/stacklok/minder/internal/util/cli/table/layouts" + minderv1 "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1" +) + +// inviteListCmd represents the list command +var inviteListCmd = &cobra.Command{ + Use: "list", + Short: "List pending invitations", + Long: `List shows all pending invitations for the current minder user`, + RunE: cli.GRPCClientWrapRunE(inviteListCommand), +} + +// inviteListCommand is the whoami subcommand +func inviteListCommand(ctx context.Context, cmd *cobra.Command, _ []string, conn *grpc.ClientConn) error { + client := minderv1.NewUserServiceClient(conn) + + // No longer print usage on returned error, since we've parsed our inputs + // See https://github.com/spf13/cobra/issues/340#issuecomment-374617413 + cmd.SilenceUsage = true + + format := viper.GetString("output") + + res, err := client.ListInvitations(ctx, &minderv1.ListInvitationsRequest{}) + if err != nil { + return cli.MessageAndError("Error listing invitations", err) + } + + switch format { + case app.JSON: + out, err := util.GetJsonFromProto(res) + if err != nil { + return cli.MessageAndError("Error getting json from proto", err) + } + cmd.Println(out) + case app.YAML: + out, err := util.GetYamlFromProto(res) + if err != nil { + return cli.MessageAndError("Error getting yaml from proto", err) + } + cmd.Println(out) + case app.Table: + t := table.New(table.Simple, layouts.Default, []string{"Sponsor", "Project", "Role", "Code"}) + for _, v := range res.Invitations { + t.AddRow(v.SponsorDisplay, v.Project, v.Role, v.Code) + } + t.Render() + default: + return fmt.Errorf("unsupported output format: %s", format) + } + return nil +} + +func init() { + inviteCmd.AddCommand(inviteListCmd) + + inviteListCmd.Flags().StringP("output", "o", app.Table, + fmt.Sprintf("Output format (one of %s)", strings.Join(app.SupportedOutputFormats(), ","))) +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 11d0cdacbf..9879b7cb61 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -20,6 +20,7 @@ import ( "github.com/stacklok/minder/cmd/cli/app" _ "github.com/stacklok/minder/cmd/cli/app/artifact" _ "github.com/stacklok/minder/cmd/cli/app/auth" + _ "github.com/stacklok/minder/cmd/cli/app/auth/invite" _ "github.com/stacklok/minder/cmd/cli/app/auth/offline_token" _ "github.com/stacklok/minder/cmd/cli/app/docs" _ "github.com/stacklok/minder/cmd/cli/app/profile"