Files
infisical/cli/packages/cmd/tokens.go
2023-09-28 19:27:38 -07:00

191 lines
6.6 KiB
Go

/*
Copyright (c) 2023 Infisical Inc.
*/
package cmd
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"strings"
"github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/crypto"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/go-resty/resty/v2"
"github.com/spf13/cobra"
)
var tokensCmd = &cobra.Command{
Use: "service-token",
Short: "Manage service tokens",
DisableFlagsInUseLine: true,
Example: "infisical service-token",
Args: cobra.ExactArgs(0),
PreRun: func(cmd *cobra.Command, args []string) {
util.RequireLogin()
},
Run: func(cmd *cobra.Command, args []string) {
},
}
var tokensCreateCmd = &cobra.Command{
Use: "create",
Short: "Used to create service tokens",
DisableFlagsInUseLine: true,
Example: "infisical service-token create",
Args: cobra.ExactArgs(0),
PreRun: func(cmd *cobra.Command, args []string) {
util.RequireLogin()
},
Run: func(cmd *cobra.Command, args []string) {
// get plain text workspace key
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
if err != nil {
util.HandleError(err, "Unable to retrieve your logged in your details. Please login in then try again")
}
if loggedInUserDetails.LoginExpired {
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
}
tokenOnly, err := cmd.Flags().GetBool("token-only")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
workspaceId, err := cmd.Flags().GetString("projectId")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
if workspaceId == "" {
configFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag")
}
workspaceId = configFile.WorkspaceId
}
serviceTokenName, err := cmd.Flags().GetString("name")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
expireSeconds, err := cmd.Flags().GetInt("expiry-seconds")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
scopes, err := cmd.Flags().GetStringSlice("scope")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
if len(scopes) == 0 {
util.PrintErrorMessageAndExit("You must define the environments and paths your service token should have access to via the --scope flag")
}
permissions := []api.ScopePermission{}
for _, scope := range scopes {
parts := strings.Split(scope, ":")
if len(parts) != 2 {
fmt.Println("--scope flag is malformed. Each scope flag should be in the following format: <env-slug>:<folder-path>")
return
}
permissions = append(permissions, api.ScopePermission{Environment: parts[0], SecretPath: parts[1]})
}
accessLevels, err := cmd.Flags().GetStringSlice("access-level")
if err != nil {
util.HandleError(err, "Unable to parse flag accessLevels")
}
if len(accessLevels) == 0 {
util.PrintErrorMessageAndExit("You must define whether your service token can be used to read and or write via the --access-level flag")
}
for _, accessLevel := range accessLevels {
if accessLevel != "read" && accessLevel != "write" {
util.PrintErrorMessageAndExit("--access-level can only be of values read and write")
}
}
workspaceKey, err := util.GetPlainTextWorkspaceKey(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceId)
if err != nil {
util.HandleError(err, "Unable to get workspace key needed to create service token")
}
newWorkspaceEncryptionKey := make([]byte, 16)
_, err = rand.Read(newWorkspaceEncryptionKey)
if err != nil {
util.HandleError(err)
}
newWorkspaceEncryptionKeyHexFormat := hex.EncodeToString(newWorkspaceEncryptionKey)
// encrypt the workspace key symmetrically
encryptedDetails, err := crypto.EncryptSymmetric(workspaceKey, []byte(newWorkspaceEncryptionKeyHexFormat))
if err != nil {
util.HandleError(err)
}
// make a call to the api to save the encrypted symmetric key details
httpClient := resty.New()
httpClient.SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken).
SetHeader("Accept", "application/json")
createServiceTokenResponse, err := api.CallCreateServiceToken(httpClient, api.CreateServiceTokenRequest{
Name: serviceTokenName,
WorkspaceId: workspaceId,
Scopes: permissions,
ExpiresIn: expireSeconds,
EncryptedKey: base64.StdEncoding.EncodeToString(encryptedDetails.CipherText),
Iv: base64.StdEncoding.EncodeToString(encryptedDetails.Nonce),
Tag: base64.StdEncoding.EncodeToString(encryptedDetails.AuthTag),
RandomBytes: newWorkspaceEncryptionKeyHexFormat,
Permissions: accessLevels,
})
if err != nil {
util.HandleError(err, "Unable to create service token")
}
serviceToken := createServiceTokenResponse.ServiceToken + "." + newWorkspaceEncryptionKeyHexFormat
if tokenOnly {
fmt.Println(serviceToken)
} else {
printablePermission := []string{}
for _, permission := range permissions {
printablePermission = append(printablePermission, fmt.Sprintf("([environment: %v] [path: %v])", permission.Environment, permission.SecretPath))
}
fmt.Printf("New service token created\n")
fmt.Printf("Name: %v\n", serviceTokenName)
fmt.Printf("Project ID: %v\n", workspaceId)
fmt.Printf("Access type: [%v]\n", strings.Join(accessLevels, ", "))
fmt.Printf("Permission(s): %v\n", strings.Join(printablePermission, ", "))
fmt.Printf("Service Token: %v\n", serviceToken)
}
},
}
func init() {
tokensCreateCmd.Flags().String("projectId", "", "The project ID you'd like to create the service token for. Default: will use linked Infisical project in .infisical.json")
tokensCreateCmd.Flags().StringSliceP("scope", "s", []string{}, "Environment and secret path. Example format: <env-slug>:<folder-path>")
tokensCreateCmd.Flags().StringP("name", "n", "Service token generated via CLI", "Service token name")
tokensCreateCmd.Flags().StringSliceP("access-level", "a", []string{}, "The type of access the service token should have. Can be 'read' and or 'write'")
tokensCreateCmd.Flags().Bool("token-only", false, "When true, only the service token will be printed")
tokensCreateCmd.Flags().IntP("expiry-seconds", "e", 86400, "Set the service token's expiration time in seconds from now. To never expire set to zero. Default: 1 day ")
tokensCmd.AddCommand(tokensCreateCmd)
rootCmd.AddCommand(tokensCmd)
}