mirror of
https://github.com/safedep/vet.git
synced 2025-12-11 09:25:44 -06:00
feat: Add API key management command
This commit is contained in:
parent
d8d94b7c1a
commit
06b080a81c
4
auth.go
4
auth.go
@ -116,9 +116,7 @@ func verifyAuthCommand() *cobra.Command {
|
|||||||
ui.PrintSuccess("Running in Community Mode")
|
ui.PrintSuccess("Running in Community Mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
failOnError("auth/verify", auth.Verify(&auth.VerifyConfig{
|
failOnError("auth/verify", auth.Verify())
|
||||||
ControlPlaneApiUrl: authControlPlaneApiBaseUrl,
|
|
||||||
}))
|
|
||||||
|
|
||||||
ui.PrintSuccess("Authentication key is valid!")
|
ui.PrintSuccess("Authentication key is valid!")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
83
cmd/cloud/key.go
Normal file
83
cmd/cloud/key.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/safedep/vet/internal/auth"
|
||||||
|
"github.com/safedep/vet/internal/ui"
|
||||||
|
"github.com/safedep/vet/pkg/cloud"
|
||||||
|
"github.com/safedep/vet/pkg/common/logger"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
keyName string
|
||||||
|
keyDescription string
|
||||||
|
keyExpiresIn int
|
||||||
|
)
|
||||||
|
|
||||||
|
func newKeyCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "key",
|
||||||
|
Short: "Manage API keys",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return cmd.Help()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.AddCommand(newKeyCreateCommand())
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func newKeyCreateCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "create",
|
||||||
|
Short: "Create a new API key",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := executeCreateKey()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Failed to create API key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVar(&keyName, "name", "", "Name of the API key")
|
||||||
|
cmd.Flags().StringVar(&keyDescription, "description", "", "Description of the API key")
|
||||||
|
cmd.Flags().IntVar(&keyExpiresIn, "expires-in", 30,
|
||||||
|
"Number of days after which the API key will expire")
|
||||||
|
|
||||||
|
_ = cmd.MarkFlagRequired("name")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeCreateKey() error {
|
||||||
|
client, err := auth.ControlPlaneClientConnection("vet-cloud-key-create")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyService, err := cloud.NewApiKeyService(client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := keyService.CreateApiKey(&cloud.CreateApiKeyRequest{
|
||||||
|
Name: keyName,
|
||||||
|
Desc: keyDescription,
|
||||||
|
ExpiryInDays: keyExpiresIn,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.PrintSuccess("API key created successfully.")
|
||||||
|
ui.PrintSuccess("Key: %s", key.Key)
|
||||||
|
ui.PrintSuccess("Expires at: %s", key.ExpiresAt.Format(time.RFC3339))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ func NewCloudCommand() *cobra.Command {
|
|||||||
cmd.AddCommand(newQueryCommand())
|
cmd.AddCommand(newQueryCommand())
|
||||||
cmd.AddCommand(newPingCommand())
|
cmd.AddCommand(newPingCommand())
|
||||||
cmd.AddCommand(newWhoamiCommand())
|
cmd.AddCommand(newWhoamiCommand())
|
||||||
|
cmd.AddCommand(newKeyCommand())
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,66 +1,24 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import "github.com/safedep/vet/pkg/cloud"
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
apierr "github.com/safedep/dry/errors"
|
// Verify authentication to the data plane using
|
||||||
"github.com/safedep/dry/utils"
|
// API key and Ping Service.
|
||||||
"github.com/safedep/vet/gen/cpv1"
|
func Verify() error {
|
||||||
"github.com/safedep/vet/pkg/common/logger"
|
conn, err := SyncClientConnection("vet-auth-verify")
|
||||||
)
|
|
||||||
|
|
||||||
type VerifyConfig struct {
|
|
||||||
ControlPlaneApiUrl string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify function takes config and current API key available
|
|
||||||
// from this package and returns an error if auth is invalid
|
|
||||||
func Verify(config *VerifyConfig) error {
|
|
||||||
if CommunityMode() {
|
|
||||||
logger.Infof("Skipping auth verify due to community mode enabled")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if ApiKey() == "" {
|
|
||||||
return fmt.Errorf("API key is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("Verifying auth token using Control Plane: %s", config.ControlPlaneApiUrl)
|
|
||||||
|
|
||||||
client, err := cpv1.NewClientWithResponses(config.ControlPlaneApiUrl)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authKeyApplier := func(ctx context.Context, req *http.Request) error {
|
pingService, err := cloud.NewPingService(conn)
|
||||||
req.Header.Set("Authorization", ApiKey())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.GetApiCredentialIntrospectionWithResponse(context.Background(),
|
|
||||||
authKeyApplier)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode() != http.StatusOK {
|
_, err = pingService.Ping()
|
||||||
if err, ok := apierr.UnmarshalApiError(resp.Body); ok {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
return fmt.Errorf("unexpected status code:%d from control plane",
|
|
||||||
resp.HTTPResponse.StatusCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.JSON200 == nil {
|
|
||||||
return fmt.Errorf("invalid nil response from server")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("Current auth token is valid with expiry: %s",
|
|
||||||
utils.SafelyGetValue(resp.JSON200.Expiry))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
47
pkg/cloud/key.go
Normal file
47
pkg/cloud/key.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/controltower/v1/controltowerv1grpc"
|
||||||
|
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type apiKeyService struct {
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApiKeyService(conn *grpc.ClientConn) (*apiKeyService, error) {
|
||||||
|
return &apiKeyService{conn: conn}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateApiKeyRequest struct {
|
||||||
|
Name string
|
||||||
|
Desc string
|
||||||
|
ExpiryInDays int
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateApiKeyResponse struct {
|
||||||
|
Key string
|
||||||
|
ExpiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *apiKeyService) CreateApiKey(req *CreateApiKeyRequest) (*CreateApiKeyResponse, error) {
|
||||||
|
keyService := controltowerv1grpc.NewApiKeyServiceClient(a.conn)
|
||||||
|
res, err := keyService.CreateApiKey(context.Background(), &controltowerv1.CreateApiKeyRequest{
|
||||||
|
Name: req.Name,
|
||||||
|
Description: &req.Desc,
|
||||||
|
ExpiryDays: int32(req.ExpiryInDays),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CreateApiKeyResponse{
|
||||||
|
Key: res.GetKey(),
|
||||||
|
ExpiresAt: res.GetExpiresAt().AsTime(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
4
scan.go
4
scan.go
@ -178,9 +178,7 @@ func listParsersCommand() *cobra.Command {
|
|||||||
|
|
||||||
func startScan() {
|
func startScan() {
|
||||||
if !disableAuthVerifyBeforeScan {
|
if !disableAuthVerifyBeforeScan {
|
||||||
err := auth.Verify(&auth.VerifyConfig{
|
err := auth.Verify()
|
||||||
ControlPlaneApiUrl: auth.ControlTowerUrl(),
|
|
||||||
})
|
|
||||||
|
|
||||||
// We will fallback to community mode by default to provide
|
// We will fallback to community mode by default to provide
|
||||||
// a seamless user experience
|
// a seamless user experience
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user