Skip to content

Commit

Permalink
Merge pull request #263 from safedep/feat/cloud-apikey-management
Browse files Browse the repository at this point in the history
feat: Add Support for Cloud API Key Management
  • Loading branch information
abhisek authored Oct 21, 2024
2 parents 298ddbe + 3c4f427 commit 9feafdb
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 35 deletions.
108 changes: 108 additions & 0 deletions cmd/cloud/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ var (
keyName string
keyDescription string
keyExpiresIn int

listKeysName string
listKeysIncludeExpired bool
listKeysOnlyMine bool

deleteKeyId string
)

func newKeyCommand() *cobra.Command {
Expand All @@ -26,10 +32,112 @@ func newKeyCommand() *cobra.Command {
}

cmd.AddCommand(newKeyCreateCommand())
cmd.AddCommand(newListKeyCommand())
cmd.AddCommand(newDeleteKeyCommand())

return cmd
}

func newDeleteKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Short: "Delete an API key",
RunE: func(cmd *cobra.Command, args []string) error {
err := executeDeleteKey()
if err != nil {
logger.Errorf("Failed to delete API key: %v", err)
}

return nil
},
}

cmd.Flags().StringVar(&deleteKeyId, "id", "", "ID of the API key to delete")
_ = cmd.MarkFlagRequired("id")

return cmd
}

func executeDeleteKey() error {
client, err := auth.ControlPlaneClientConnection("vet-cloud-key-delete")
if err != nil {
return err
}

keyService, err := cloud.NewApiKeyService(client)
if err != nil {
return err
}

err = keyService.DeleteKey(deleteKeyId)
if err != nil {
return err
}

ui.PrintSuccess("API key deleted successfully.")
return nil
}

func newListKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List API keys",
RunE: func(cmd *cobra.Command, args []string) error {
err := executeListKeys()
if err != nil {
logger.Errorf("Failed to list API keys: %v", err)
}

return nil
},
}

cmd.Flags().StringVar(&listKeysName, "name", "",
"List keys with partial match on the name")
cmd.Flags().BoolVar(&listKeysIncludeExpired, "include-expired", false,
"Include expired keys in the list")
cmd.Flags().BoolVar(&listKeysOnlyMine, "only-mine", false,
"List only keys created by the current user")

return cmd
}

func executeListKeys() error {
client, err := auth.ControlPlaneClientConnection("vet-cloud-key-list")
if err != nil {
return err
}

keyService, err := cloud.NewApiKeyService(client)
if err != nil {
return err
}

keys, err := keyService.ListKeys(&cloud.ListApiKeyRequest{
Name: listKeysName,
IncludeExpired: listKeysIncludeExpired,
OnlyMine: listKeysOnlyMine,
})
if err != nil {
return err
}

if len(keys.Keys) == 0 {
ui.PrintSuccess("No API keys found.")
return nil
}

tbl := ui.NewTabler(ui.TablerConfig{})
tbl.AddHeader("ID", "Name", "Expires At", "Description")

for _, key := range keys.Keys {
expiresAt := key.ExpiresAt.In(time.Local).Format(time.RFC822)
tbl.AddRow(key.ID, key.Name, expiresAt, key.Desc)
}

return tbl.Finish()
}

func newKeyCreateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Expand Down
12 changes: 11 additions & 1 deletion cmd/cloud/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import (
"github.com/spf13/cobra"
)

var tenantDomain string
var (
tenantDomain string
outputCSV string
outputMarkdown string
)

func NewCloudCommand() *cobra.Command {
cmd := &cobra.Command{
Expand All @@ -19,6 +23,12 @@ func NewCloudCommand() *cobra.Command {
cmd.PersistentFlags().StringVar(&tenantDomain, "tenant", "",
"Tenant domain to use for the command")

cmd.PersistentFlags().StringVar(&outputCSV, "csv", "",
"Output table views to a CSV file")

cmd.PersistentFlags().StringVar(&outputMarkdown, "markdown", "",
"Output table views to a Markdown file")

cmd.AddCommand(newCloudLoginCommand())
cmd.AddCommand(newRegisterCommand())
cmd.AddCommand(newQueryCommand())
Expand Down
37 changes: 15 additions & 22 deletions cmd/cloud/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package cloud

import (
"errors"
"os"
"sort"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/cloud/query"
Expand Down Expand Up @@ -86,11 +84,12 @@ func getQuerySchema() error {
return err
}

tbl := table.NewWriter()
tbl.SetOutputMirror(os.Stdout)
tbl.SetStyle(table.StyleLight)
tbl := ui.NewTabler(ui.TablerConfig{
CsvPath: outputCSV,
MarkdownPath: outputMarkdown,
})

tbl.AppendHeader(table.Row{"Name", "Column Name", "Selectable", "Filterable", "Reference"})
tbl.AddHeader("Name", "Column Name", "Selectable", "Filterable", "Reference")

schemas := response.GetSchemas()
for _, schema := range schemas {
Expand All @@ -102,20 +101,15 @@ func getQuerySchema() error {
})

for _, column := range columns {
tbl.AppendRow(table.Row{
schemaName,
tbl.AddRow(schemaName,
column.GetName(),
column.GetSelectable(),
column.GetFilterable(),
column.GetReferenceUrl(),
})
column.GetReferenceUrl())
}

tbl.AppendSeparator()
}

tbl.Render()
return nil
return tbl.Finish()
}

func executeQuery() error {
Expand All @@ -142,9 +136,10 @@ func executeQuery() error {
}

func renderQueryResponseAsTable(response *query.QueryResponse) error {
tbl := table.NewWriter()
tbl.SetOutputMirror(os.Stdout)
tbl.SetStyle(table.StyleLight)
tbl := ui.NewTabler(ui.TablerConfig{
CsvPath: outputCSV,
MarkdownPath: outputMarkdown,
})

if response.Count() == 0 {
logger.Infof("No results found")
Expand All @@ -166,7 +161,7 @@ func renderQueryResponseAsTable(response *query.QueryResponse) error {
headerRow = append(headerRow, header)
}

tbl.AppendHeader(headerRow)
tbl.AddHeader(headerRow...)

// Ensure we have a consistent order of columns
response.ForEachRow(func(row *query.QueryRow) {
Expand All @@ -175,10 +170,8 @@ func renderQueryResponseAsTable(response *query.QueryResponse) error {
rowValues = append(rowValues, row.GetField(header))
}

tbl.AppendRow(rowValues)
tbl.AppendSeparator()
tbl.AddRow(rowValues...)
})

tbl.Render()
return nil
return tbl.Finish()
}
20 changes: 14 additions & 6 deletions cmd/cloud/whoami.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package cloud

import (
"fmt"

controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/controltower/v1"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/cloud"
Expand Down Expand Up @@ -41,14 +44,19 @@ func executeWhoami() error {
return err
}

ui.PrintSuccess("Authenticated as: %s <%s>", res.GetUser().GetName(),
res.GetUser().GetEmail())
tbl := ui.NewTabler(ui.TablerConfig{})

ui.PrintSuccess("Has access to the following tenants:")
tbl.AddHeader("Email", "Tenant", "Access Level")
for _, access := range res.GetAccess() {
ui.PrintSuccess(" - %s [%d]", access.GetTenant().GetDomain(),
access.GetLevel())
accessName := "UNSPECIFIED"
if name, ok := controltowerv1.AccessLevel_name[int32(access.GetLevel())]; ok {
accessName = name
}

tbl.AddRow(res.GetUser().GetEmail(),
access.GetTenant().GetDomain(),
fmt.Sprintf("%s (%d)", accessName, access.GetRole()))
}

return nil
return tbl.Finish()
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module github.com/safedep/vet
go 1.23.2

require (
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241018171312-dab1d3391113.1
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241018171312-dab1d3391113.1
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241021065218-021c29274fc6.1
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241021065218-021c29274fc6.1
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/CycloneDX/cyclonedx-go v0.9.1
github.com/anchore/syft v1.14.1
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.1-20240920164238-5a7b106cbb87.1 h1:9wP6ZZYWnF2Z0TxmII7m3XNykxnP4/w8oXeth6ekcRI=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.1-20240920164238-5a7b106cbb87.1/go.mod h1:Duw/9JoXkXIydyASnLYIiufkzySThoqavOsF+IihqvM=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241018171312-dab1d3391113.1 h1:Iya58BNJyNN2f7xMGRTd69UqN+PlIcDZroh5zRPeQdo=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241018171312-dab1d3391113.1/go.mod h1:aEp9GzEA+av+idM44tVG+u8WYl/nHBYLDqGpI9gFKtk=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241018171312-dab1d3391113.1 h1:HAnfxnohw6+AWaIiZoZtlvjEk/ABw01TmGFu8ygEbGI=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241018171312-dab1d3391113.1/go.mod h1:WCxZaBpYxgWnSpauuzVhzbJawAp6uPXJPN5tbDpceQ0=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241021065218-021c29274fc6.1 h1:g7y1C4EeDQpg4rFciYfXxmeW28UX+ySv5nQbBFpvbiA=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241021065218-021c29274fc6.1/go.mod h1:+Fs1Kwxdn+N0xgYXBtKIw7M7BGUtZmkOND8V8zDO/Ys=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241021065218-021c29274fc6.1 h1:c9YRtuOJh9H9aR/nxFqjDyCzt03DdZfllX64aW/O4E4=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241021065218-021c29274fc6.1/go.mod h1:WCxZaBpYxgWnSpauuzVhzbJawAp6uPXJPN5tbDpceQ0=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
Expand Down
97 changes: 97 additions & 0 deletions internal/ui/tabler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package ui

import (
"os"

"github.com/jedib0t/go-pretty/v6/table"
)

type TablerConfig struct {
CsvPath string
MarkdownPath string
SkipStdoutMirror bool
SkipAutoAddSeperator bool
}

type tabler struct {
table table.Writer
config TablerConfig
}

func NewTabler(config TablerConfig) *tabler {
tbl := table.NewWriter()
tbl.SetStyle(table.StyleLight)

return &tabler{
table: tbl,
config: config,
}
}

func (t *tabler) AddHeader(header ...interface{}) {
t.table.AppendHeader(header)
t.checkAddSeparator()
}

func (t *tabler) AddRow(row ...interface{}) {
t.table.AppendRow(row)
t.checkAddSeparator()
}

func (t *tabler) checkAddSeparator() {
if !t.config.SkipAutoAddSeperator {
t.table.AppendSeparator()
}
}

func (t *tabler) Finish() error {
if t.config.CsvPath != "" {
if err := t.renderCsvFile(t.config.CsvPath); err != nil {
return err
}
}

if t.config.MarkdownPath != "" {
if err := t.renderMarkdownFile(t.config.MarkdownPath); err != nil {
return err
}
}

if !t.config.SkipStdoutMirror {
if _, err := os.Stdout.WriteString(t.table.Render()); err != nil {
return err
}
}

return nil
}

func (t *tabler) renderCsvFile(path string) error {
f, err := os.Create(path)
if err != nil {
return err
}

defer f.Close()
_, err = f.WriteString(t.table.RenderCSV())
if err != nil {
return err
}

return nil
}

func (t *tabler) renderMarkdownFile(path string) error {
f, err := os.Create(path)
if err != nil {
return err
}

defer f.Close()
_, err = f.WriteString(t.table.RenderMarkdown())
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit 9feafdb

Please sign in to comment.