diff --git a/internal/cli/cmd/auth/cmd.go b/internal/cli/cmd/auth/cmd.go index f915d7293..dbc323984 100644 --- a/internal/cli/cmd/auth/cmd.go +++ b/internal/cli/cmd/auth/cmd.go @@ -19,6 +19,7 @@ func NewAuthCmd() *cobra.Command { cmd.AddCommand(newTrustAwsCognitoCmd()) cmd.AddCommand(NewIssueIdTokenCmd()) cmd.AddCommand(NewExchangeOIDCTokenCmd()) + cmd.AddCommand(NewGenerateDevTokenCmd()) return cmd } diff --git a/internal/cli/cmd/auth/devtoken.go b/internal/cli/cmd/auth/devtoken.go new file mode 100644 index 000000000..5267206b4 --- /dev/null +++ b/internal/cli/cmd/auth/devtoken.go @@ -0,0 +1,46 @@ +// Copyright 2022 Namespace Labs Inc; All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package auth + +import ( + "context" + "fmt" + "os" + + "github.com/spf13/cobra" + "namespacelabs.dev/foundation/internal/cli/fncobra" + "namespacelabs.dev/foundation/internal/console" + "namespacelabs.dev/foundation/internal/fnapi" + "namespacelabs.dev/foundation/internal/fnerrors" +) + +func NewGenerateDevTokenCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "generate-dev-token", + Short: "Generate a Namespace Cloud token for development purposes.", + Args: cobra.NoArgs, + Hidden: true, + } + + outputPath := cmd.Flags().String("output_to", "", "If specified, write the access token to this path.") + + return fncobra.Cmd(cmd).Do(func(ctx context.Context) error { + res, err := fnapi.IssueDevelopmentToken(ctx) + if err != nil { + return err + } + + if *outputPath != "" { + if err := os.WriteFile(*outputPath, []byte(res.DevelopmentToken), 0644); err != nil { + return fnerrors.New("failed to write %q: %w", *outputPath, err) + } + + return nil + } + + fmt.Fprintln(console.Stdout(ctx), res.DevelopmentToken) + return nil + }) +} diff --git a/internal/fnapi/tenants.go b/internal/fnapi/tenants.go index f1ae9098c..bd78ea44a 100644 --- a/internal/fnapi/tenants.go +++ b/internal/fnapi/tenants.go @@ -74,6 +74,10 @@ type IssueIngressAccessTokenResponse struct { IngressAccessToken string `json:"ingress_access_token,omitempty"` } +type IssueDevelopmentTokenResponse struct { + DevelopmentToken string `json:"development_token,omitempty"` +} + type Tenant struct { TenantId string `json:"tenant_id,omitempty"` Name string `json:"name,omitempty"` @@ -153,6 +157,17 @@ func IssueIngressAccessToken(ctx context.Context, instanceId string) (IssueIngre return res, nil } +func IssueDevelopmentToken(ctx context.Context) (IssueDevelopmentTokenResponse, error) { + req := struct{}{} + + var res IssueDevelopmentTokenResponse + if err := AuthenticatedCall(ctx, EndpointAddress, "nsl.tenants.TenantsService/IssueDevelopmentToken", req, DecodeJSONResponse(&res)); err != nil { + return IssueDevelopmentTokenResponse{}, err + } + + return res, nil +} + type TokenClaims struct { TenantID string `json:"tenant_id"` InstanceID string `json:"instance_id"`