mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-09 07:28:09 -05:00
allow to query for folders via path in backend, allow to filter by path in cli
This commit is contained in:
@@ -2,21 +2,38 @@ import { Request, Response } from 'express';
|
||||
import { Secret } from '../../models';
|
||||
import Folder from '../../models/folder';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
import { ROOT_FOLDER_PATH, getFolderPath, getParentPath, normalizePath, validateFolderName } from '../../utils/folder';
|
||||
|
||||
// TODO
|
||||
// verify workspace id/environment
|
||||
export const createFolder = async (req: Request, res: Response) => {
|
||||
const { workspaceId, environment, folderName, parentFolderId } = req.body
|
||||
if (!validateFolderName(folderName)) {
|
||||
throw BadRequestError({ message: "Folder name cannot contain spaces. Only underscore and dashes" })
|
||||
}
|
||||
|
||||
if (parentFolderId) {
|
||||
const parentFolder = await Folder.findById(parentFolderId);
|
||||
const parentFolder = await Folder.find({ environment: environment, workspace: workspaceId, id: parentFolderId });
|
||||
if (!parentFolder) {
|
||||
throw BadRequestError({ message: "The parent folder doesn't exist" })
|
||||
}
|
||||
}
|
||||
|
||||
let completePath = await getFolderPath(parentFolderId)
|
||||
if (completePath == ROOT_FOLDER_PATH) {
|
||||
completePath = ""
|
||||
}
|
||||
|
||||
const currentFolderPath = completePath + "/" + folderName // construct new path with current folder to be created
|
||||
const normalizedCurrentPath = normalizePath(currentFolderPath)
|
||||
const normalizedParentPath = getParentPath(normalizedCurrentPath)
|
||||
|
||||
const existingFolder = await Folder.findOne({
|
||||
name: folderName,
|
||||
workspace: workspaceId,
|
||||
environment: environment,
|
||||
parent: parentFolderId,
|
||||
path: normalizedCurrentPath
|
||||
});
|
||||
|
||||
if (existingFolder) {
|
||||
@@ -28,6 +45,8 @@ export const createFolder = async (req: Request, res: Response) => {
|
||||
workspace: workspaceId,
|
||||
environment: environment,
|
||||
parent: parentFolderId,
|
||||
path: normalizedCurrentPath,
|
||||
parentPath: normalizedParentPath
|
||||
});
|
||||
|
||||
await newFolder.save();
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
BatchSecretRequest,
|
||||
BatchSecret
|
||||
} from '../../types/secret';
|
||||
import { getFolderPath } from '../../utils/folder';
|
||||
import { getFolderPath, getFoldersInDirectory, normalizePath } from '../../utils/folder';
|
||||
import Folder from '../../models/folder';
|
||||
|
||||
/**
|
||||
@@ -507,7 +507,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
@@ -543,8 +543,9 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
const postHogClient = getPostHogClient();
|
||||
|
||||
const { workspaceId, environment, tagSlugs } = req.query;
|
||||
const { folderId } = req.query
|
||||
const { workspaceId, environment, tagSlugs, secretsPath } = req.query;
|
||||
|
||||
const normalizedPath = normalizePath(secretsPath as string)
|
||||
const tagNamesList = typeof tagSlugs === 'string' && tagSlugs !== '' ? tagSlugs.split(',') : [];
|
||||
let userId = "" // used for getting personal secrets for user
|
||||
let userEmail = "" // used for posthog
|
||||
@@ -602,10 +603,8 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
}
|
||||
}
|
||||
|
||||
// query for secrets at root folder
|
||||
if (folderId != undefined) {
|
||||
secretQuery.folder = folderId == "" ? undefined : folderId
|
||||
}
|
||||
// Add path to secrets query
|
||||
secretQuery.path = normalizedPath
|
||||
|
||||
if (hasWriteOnlyAccess) {
|
||||
secrets = await Secret.find(secretQuery).select("secretKeyCiphertext secretKeyIV secretKeyTag")
|
||||
@@ -630,14 +629,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
ipAddress: req.ip
|
||||
});
|
||||
|
||||
let folders: any[] = []
|
||||
if (folderId != undefined) {
|
||||
folders = await Folder.find({
|
||||
workspace: workspaceId,
|
||||
environment: environment,
|
||||
parent: folderId == "" ? undefined : folderId // undefined means root
|
||||
})
|
||||
}
|
||||
const folders = await getFoldersInDirectory(workspaceId as string, environment as string, normalizedPath)
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
|
||||
@@ -19,6 +19,14 @@ const folderSchema = new Schema({
|
||||
ref: 'Folder',
|
||||
required: false, // optional for root folders
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
parentPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
}, {
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import Folder from "../models/folder";
|
||||
|
||||
export const ROOT_FOLDER_PATH = "/"
|
||||
|
||||
export const getFolderPath = async (folderId: string) => {
|
||||
let currentFolder = await Folder.findById(folderId);
|
||||
const pathSegments = [];
|
||||
@@ -10,4 +12,76 @@ export const getFolderPath = async (folderId: string) => {
|
||||
}
|
||||
|
||||
return '/' + pathSegments.join('/');
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
Returns the folder ID associated with the specified secret path in the given workspace and environment.
|
||||
@param workspaceId - The ID of the workspace to search in.
|
||||
@param environment - The environment to search in.
|
||||
@param secretPath - The secret path to search for.
|
||||
@returns The folder ID associated with the specified secret path, or undefined if the path is at the root folder level.
|
||||
@throws Error if the specified secret path is not found.
|
||||
*/
|
||||
export const getFolderIdFromPath = async (workspaceId: string, environment: string, secretPath: string) => {
|
||||
const secretPathParts = secretPath.split("/").filter(path => path != "")
|
||||
if (secretPathParts.length <= 1) {
|
||||
return undefined // root folder, so no folder id
|
||||
}
|
||||
|
||||
const folderId = await Folder.find({ path: secretPath, workspace: workspaceId, environment: environment })
|
||||
if (!folderId) {
|
||||
throw Error("Secret path not found")
|
||||
}
|
||||
|
||||
return folderId
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up a path by removing empty parts, duplicate slashes,
|
||||
* and ensuring it starts with ROOT_FOLDER_PATH.
|
||||
* @param path - The input path to clean up.
|
||||
* @returns The cleaned-up path string.
|
||||
*/
|
||||
export const normalizePath = (path: string) => {
|
||||
if (path == undefined || path == "" || path == ROOT_FOLDER_PATH) {
|
||||
return ROOT_FOLDER_PATH
|
||||
}
|
||||
|
||||
const pathParts = path.split("/").filter(part => part != "")
|
||||
const cleanPathString = ROOT_FOLDER_PATH + pathParts.join("/")
|
||||
|
||||
return cleanPathString
|
||||
}
|
||||
|
||||
export const getFoldersInDirectory = async (workspaceId: string, environment: string, pathString: string) => {
|
||||
const normalizedPath = normalizePath(pathString)
|
||||
const foldersInDirectory = await Folder.find({
|
||||
workspace: workspaceId,
|
||||
environment: environment,
|
||||
parentPath: normalizedPath,
|
||||
});
|
||||
|
||||
return foldersInDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent path of the given path.
|
||||
* @param path - The input path.
|
||||
* @returns The parent path string.
|
||||
*/
|
||||
export const getParentPath = (path: string) => {
|
||||
const normalizedPath = normalizePath(path);
|
||||
const folderParts = normalizedPath.split('/').filter(part => part !== '');
|
||||
|
||||
let folderParent = ROOT_FOLDER_PATH;
|
||||
if (folderParts.length > 1) {
|
||||
folderParent = ROOT_FOLDER_PATH + folderParts.slice(0, folderParts.length - 1).join('/');
|
||||
}
|
||||
|
||||
return folderParent;
|
||||
}
|
||||
|
||||
export const validateFolderName = (folderName: string) => {
|
||||
const validNameRegex = /^[a-zA-Z0-9-_]+$/;
|
||||
return validNameRegex.test(folderName);
|
||||
}
|
||||
@@ -115,6 +115,7 @@ func CallGetSecretsV2(httpClient *resty.Client, request GetEncryptedSecretsV2Req
|
||||
SetQueryParam("environment", request.Environment).
|
||||
SetQueryParam("workspaceId", request.WorkspaceId).
|
||||
SetQueryParam("tagSlugs", request.TagSlugs).
|
||||
SetQueryParam("secretsPath", request.SecretPath).
|
||||
Get(fmt.Sprintf("%v/v2/secrets", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -198,35 +198,51 @@ type GetEncryptedSecretsV2Request struct {
|
||||
Environment string `json:"environment"`
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
TagSlugs string `json:"tagSlugs"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
FolderId string `json:"folderId"`
|
||||
}
|
||||
|
||||
type Folders struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Workspace string `json:"workspace"`
|
||||
Environment string `json:"environment"`
|
||||
Parent string `json:"parent"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type Secrets struct {
|
||||
ID string `json:"_id"`
|
||||
Version int `json:"version"`
|
||||
Workspace string `json:"workspace"`
|
||||
Type string `json:"type"`
|
||||
Environment string `json:"environment"`
|
||||
SecretKeyCiphertext string `json:"secretKeyCiphertext"`
|
||||
SecretKeyIV string `json:"secretKeyIV"`
|
||||
SecretKeyTag string `json:"secretKeyTag"`
|
||||
SecretValueCiphertext string `json:"secretValueCiphertext"`
|
||||
SecretValueIV string `json:"secretValueIV"`
|
||||
SecretValueTag string `json:"secretValueTag"`
|
||||
SecretCommentCiphertext string `json:"secretCommentCiphertext"`
|
||||
SecretCommentIV string `json:"secretCommentIV"`
|
||||
SecretCommentTag string `json:"secretCommentTag"`
|
||||
V int `json:"__v"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
User string `json:"user,omitempty"`
|
||||
Tags []struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Workspace string `json:"workspace"`
|
||||
} `json:"tags"`
|
||||
}
|
||||
|
||||
type GetEncryptedSecretsV2Response struct {
|
||||
Secrets []struct {
|
||||
ID string `json:"_id"`
|
||||
Version int `json:"version"`
|
||||
Workspace string `json:"workspace"`
|
||||
Type string `json:"type"`
|
||||
Environment string `json:"environment"`
|
||||
SecretKeyCiphertext string `json:"secretKeyCiphertext"`
|
||||
SecretKeyIV string `json:"secretKeyIV"`
|
||||
SecretKeyTag string `json:"secretKeyTag"`
|
||||
SecretValueCiphertext string `json:"secretValueCiphertext"`
|
||||
SecretValueIV string `json:"secretValueIV"`
|
||||
SecretValueTag string `json:"secretValueTag"`
|
||||
SecretCommentCiphertext string `json:"secretCommentCiphertext"`
|
||||
SecretCommentIV string `json:"secretCommentIV"`
|
||||
SecretCommentTag string `json:"secretCommentTag"`
|
||||
V int `json:"__v"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
User string `json:"user,omitempty"`
|
||||
Tags []struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Workspace string `json:"workspace"`
|
||||
} `json:"tags"`
|
||||
} `json:"secrets"`
|
||||
Secrets []Secrets `json:"secrets"`
|
||||
|
||||
Folders []Folders `json:"folders"`
|
||||
}
|
||||
|
||||
type GetServiceTokenDetailsResponse struct {
|
||||
|
||||
@@ -74,7 +74,12 @@ var exportCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, WorkspaceId: projectId})
|
||||
secretsPath, err := cmd.Flags().GetString("path")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, _, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, WorkspaceId: projectId, Path: secretsPath})
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to fetch secrets")
|
||||
}
|
||||
@@ -112,6 +117,7 @@ func init() {
|
||||
exportCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
exportCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs")
|
||||
exportCmd.Flags().String("projectId", "", "manually set the projectId to fetch secrets from")
|
||||
exportCmd.Flags().String("path", "/", "The path to the folder where secrets are located. Defaults to root folder")
|
||||
}
|
||||
|
||||
// Format according to the format flag
|
||||
|
||||
@@ -82,7 +82,12 @@ var runCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs})
|
||||
secretsPath, err := cmd.Flags().GetString("path")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, _, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, Path: secretsPath})
|
||||
|
||||
if err != nil {
|
||||
util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid")
|
||||
@@ -182,6 +187,7 @@ func init() {
|
||||
runCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
|
||||
runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")")
|
||||
runCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs ")
|
||||
runCmd.Flags().String("path", "/", "The path to the folder where secrets are located. Defaults to root folder")
|
||||
}
|
||||
|
||||
// Will execute a single command and pass in the given secrets into the process
|
||||
|
||||
@@ -44,6 +44,11 @@ var secretsCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secretsPath, err := cmd.Flags().GetString("path")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
shouldExpandSecrets, err := cmd.Flags().GetBool("expand")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
@@ -54,7 +59,8 @@ var secretsCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs})
|
||||
normalizedPath := util.NormalizePath(secretsPath)
|
||||
secrets, folders, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, Path: normalizedPath})
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
@@ -63,6 +69,10 @@ var secretsCmd = &cobra.Command{
|
||||
secrets = util.SubstituteSecrets(secrets)
|
||||
}
|
||||
|
||||
if len(folders) > 0 {
|
||||
visualize.PrintSecretFolders(folders)
|
||||
}
|
||||
|
||||
visualize.PrintAllSecretDetails(secrets)
|
||||
},
|
||||
}
|
||||
@@ -142,7 +152,7 @@ var secretsSetCmd = &cobra.Command{
|
||||
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||
|
||||
// pull current secrets
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName})
|
||||
secrets, _, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName})
|
||||
if err != nil {
|
||||
util.HandleError(err, "unable to retrieve secrets")
|
||||
}
|
||||
@@ -263,13 +273,14 @@ var secretsSetCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// Print secret operations
|
||||
headers := [...]string{"SECRET NAME", "SECRET VALUE", "STATUS"}
|
||||
rows := [][3]string{}
|
||||
secretHeaders := [...]string{"SECRET NAME", "SECRET VALUE", "STATUS"}
|
||||
secretRows := [][3]string{}
|
||||
for _, secretOperation := range secretOperations {
|
||||
rows = append(rows, [...]string{secretOperation.SecretKey, secretOperation.SecretValue, secretOperation.SecretOperation})
|
||||
secretRows = append(secretRows, [...]string{secretOperation.SecretKey, secretOperation.SecretValue, secretOperation.SecretOperation})
|
||||
}
|
||||
|
||||
visualize.Table(headers, rows)
|
||||
// visualize.PrintSecretFolders()
|
||||
visualize.SecretsTable(secretHeaders, secretRows)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -299,11 +310,13 @@ var secretsDeleteCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to get local project details")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName})
|
||||
secrets, folders, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName})
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to fetch secrets")
|
||||
}
|
||||
|
||||
fmt.Println("folders===>", folders)
|
||||
|
||||
secretByKey := getSecretsByKeys(secrets)
|
||||
validSecretIdsToDelete := []string{}
|
||||
invalidSecretNamesThatDoNotExist := []string{}
|
||||
@@ -360,11 +373,13 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs})
|
||||
secrets, folders, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs})
|
||||
if err != nil {
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
|
||||
fmt.Println("folders===>", folders)
|
||||
|
||||
requestedSecrets := []models.SingleEnvironmentVariable{}
|
||||
|
||||
secretsMap := getSecretsByKeys(secrets)
|
||||
@@ -403,11 +418,13 @@ func generateExampleEnv(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs})
|
||||
secrets, folders, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs})
|
||||
if err != nil {
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
|
||||
fmt.Println("folders===>", folders)
|
||||
|
||||
tagsHashToSecretKey := make(map[string]int)
|
||||
slugsToFilerBy := make(map[string]int)
|
||||
|
||||
@@ -602,6 +619,7 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod
|
||||
func init() {
|
||||
|
||||
secretsGenerateExampleEnvCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
secretsCmd.Flags().String("path", "/", "The path to the folder where secrets are located. Defaults to root folder")
|
||||
secretsCmd.AddCommand(secretsGenerateExampleEnvCmd)
|
||||
|
||||
secretsGetCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
|
||||
@@ -56,4 +56,5 @@ type GetAllSecretsParameters struct {
|
||||
InfisicalToken string
|
||||
TagSlugs string
|
||||
WorkspaceId string
|
||||
Path string
|
||||
}
|
||||
|
||||
@@ -137,3 +137,36 @@ func getCurrentBranch() (string, error) {
|
||||
}
|
||||
return path.Base(strings.TrimSpace(out.String())), nil
|
||||
}
|
||||
|
||||
func GetSplitPathByDash(path string) []string {
|
||||
pathParts := strings.Split(path, "/")
|
||||
var filteredPathParts []string
|
||||
for _, s := range pathParts {
|
||||
if s != "" {
|
||||
filteredPathParts = append(filteredPathParts, s)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredPathParts
|
||||
}
|
||||
|
||||
// NormalizePath cleans up a path by removing empty parts, duplicate slashes,
|
||||
// and ensuring it starts with ROOT_FOLDER_PATH.
|
||||
func NormalizePath(path string) string {
|
||||
ROOT_FOLDER_PATH := "/"
|
||||
|
||||
if path == "" || path == ROOT_FOLDER_PATH {
|
||||
return ROOT_FOLDER_PATH
|
||||
}
|
||||
|
||||
pathParts := strings.Split(path, "/")
|
||||
nonEmptyParts := []string{}
|
||||
for _, part := range pathParts {
|
||||
if part != "" {
|
||||
nonEmptyParts = append(nonEmptyParts, part)
|
||||
}
|
||||
}
|
||||
|
||||
cleanPathString := ROOT_FOLDER_PATH + strings.Join(nonEmptyParts, "/")
|
||||
return cleanPathString
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.SingleEnvironmentVariable, error) {
|
||||
func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.SingleEnvironmentVariable, []api.Folders, error) {
|
||||
serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4)
|
||||
if len(serviceTokenParts) < 4 {
|
||||
return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
|
||||
return nil, nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
|
||||
}
|
||||
|
||||
serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])
|
||||
@@ -32,7 +32,7 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.Singl
|
||||
|
||||
serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
|
||||
return nil, nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
|
||||
}
|
||||
|
||||
encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{
|
||||
@@ -41,28 +41,28 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.Singl
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
decodedSymmetricEncryptionDetails, err := GetBase64DecodedSymmetricEncryptionDetails(serviceTokenParts[3], serviceTokenDetails.EncryptedKey, serviceTokenDetails.Iv, serviceTokenDetails.Tag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode symmetric encryption details [err=%v]", err)
|
||||
return nil, nil, fmt.Errorf("unable to decode symmetric encryption details [err=%v]", err)
|
||||
}
|
||||
|
||||
plainTextWorkspaceKey, err := crypto.DecryptSymmetric([]byte(serviceTokenParts[3]), decodedSymmetricEncryptionDetails.Cipher, decodedSymmetricEncryptionDetails.Tag, decodedSymmetricEncryptionDetails.IV)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decrypt the required workspace key")
|
||||
return nil, nil, fmt.Errorf("unable to decrypt the required workspace key")
|
||||
}
|
||||
|
||||
plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
|
||||
return nil, nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
|
||||
}
|
||||
|
||||
return plainTextSecrets, nil
|
||||
return plainTextSecrets, encryptedSecrets.Folders, nil
|
||||
}
|
||||
|
||||
func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string, tagSlugs string) ([]models.SingleEnvironmentVariable, error) {
|
||||
func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string, tagSlugs string, secretPath string) ([]models.SingleEnvironmentVariable, []api.Folders, error) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
@@ -73,7 +73,7 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
|
||||
|
||||
workspaceKeyResponse, err := api.CallGetEncryptedWorkspaceKey(httpClient, request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
|
||||
return nil, nil, fmt.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
|
||||
}
|
||||
|
||||
encryptedWorkspaceKey, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey)
|
||||
@@ -103,25 +103,26 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
|
||||
|
||||
plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||
|
||||
encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{
|
||||
encryptedSecretsAndFolders, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{
|
||||
WorkspaceId: workspaceId,
|
||||
Environment: environmentName,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretPath: secretPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets)
|
||||
plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecretsAndFolders)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
|
||||
return nil, nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
|
||||
}
|
||||
|
||||
return plainTextSecrets, nil
|
||||
return plainTextSecrets, encryptedSecretsAndFolders.Folders, nil
|
||||
}
|
||||
|
||||
func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, error) {
|
||||
func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, []api.Folders, error) {
|
||||
var infisicalToken string
|
||||
if params.InfisicalToken == "" {
|
||||
infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME)
|
||||
@@ -132,6 +133,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
||||
isConnected := CheckIsConnectedToInternet()
|
||||
var secretsToReturn []models.SingleEnvironmentVariable
|
||||
var errorToReturn error
|
||||
var folders []api.Folders
|
||||
|
||||
if infisicalToken == "" {
|
||||
if isConnected {
|
||||
@@ -144,12 +146,12 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
||||
|
||||
loggedInUserDetails, err := GetCurrentLoggedInUserDetails()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
workspaceFile, err := GetWorkSpaceFromFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if params.WorkspaceId != "" {
|
||||
@@ -159,10 +161,10 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
||||
// Verify environment
|
||||
err = ValidateEnvironmentName(params.Environment, workspaceFile.WorkspaceId, loggedInUserDetails.UserCredentials)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to validate environment name because [err=%s]", err)
|
||||
return nil, nil, fmt.Errorf("unable to validate environment name because [err=%s]", err)
|
||||
}
|
||||
|
||||
secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment, params.TagSlugs)
|
||||
secretsToReturn, folders, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment, params.TagSlugs, params.Path)
|
||||
log.Debugf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn)
|
||||
|
||||
backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32]
|
||||
@@ -182,10 +184,10 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
|
||||
|
||||
} else {
|
||||
log.Debug("Trying to fetch secrets using service token")
|
||||
secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken)
|
||||
secretsToReturn, folders, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken)
|
||||
}
|
||||
|
||||
return secretsToReturn, errorToReturn
|
||||
return secretsToReturn, folders, errorToReturn
|
||||
}
|
||||
|
||||
func ValidateEnvironmentName(environmentName string, workspaceId string, userLoggedInDetails models.UserCredentials) error {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package visualize
|
||||
|
||||
import "github.com/Infisical/infisical-merge/packages/models"
|
||||
import (
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
)
|
||||
|
||||
func PrintAllSecretDetails(secrets []models.SingleEnvironmentVariable) {
|
||||
rows := [][3]string{}
|
||||
@@ -10,5 +13,16 @@ func PrintAllSecretDetails(secrets []models.SingleEnvironmentVariable) {
|
||||
|
||||
headers := [...]string{"SECRET NAME", "SECRET VALUE", "SECRET TYPE"}
|
||||
|
||||
SecretsTable(headers, rows)
|
||||
}
|
||||
|
||||
func PrintSecretFolders(folders []api.Folders) {
|
||||
rows := [][]string{}
|
||||
for _, folder := range folders {
|
||||
rows = append(rows, []string{folder.Name})
|
||||
}
|
||||
|
||||
headers := []string{"FOLDER NAME(S)"}
|
||||
|
||||
Table(headers, rows)
|
||||
}
|
||||
|
||||
@@ -29,8 +29,36 @@ const (
|
||||
ellipsis = "…"
|
||||
)
|
||||
|
||||
// Given any number of headers and rows, this function will print out a table
|
||||
func Table(headers []string, rows [][]string) {
|
||||
t := table.NewWriter()
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.SetStyle(table.StyleLight)
|
||||
|
||||
// t.SetTitle("Title")
|
||||
t.Style().Options.DrawBorder = true
|
||||
t.Style().Options.SeparateHeader = true
|
||||
t.Style().Options.SeparateColumns = true
|
||||
|
||||
tableHeaders := table.Row{}
|
||||
for _, header := range headers {
|
||||
tableHeaders = append(tableHeaders, header)
|
||||
}
|
||||
|
||||
t.AppendHeader(tableHeaders)
|
||||
for _, row := range rows {
|
||||
tableRow := table.Row{}
|
||||
for _, val := range row {
|
||||
tableRow = append(tableRow, val)
|
||||
}
|
||||
t.AppendRow(tableRow)
|
||||
}
|
||||
|
||||
t.Render()
|
||||
}
|
||||
|
||||
// Given headers and rows, this function will print out a table
|
||||
func Table(headers [3]string, rows [][3]string) {
|
||||
func SecretsTable(headers [3]string, rows [][3]string) {
|
||||
// if we're not in a terminal or cygwin terminal, don't truncate the secret value
|
||||
shouldTruncate := isatty.IsTerminal(os.Stdout.Fd())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user