mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-25 14:05:03 +00:00
Merge branch 'main' of https://github.com/Infisical/infisical
This commit is contained in:
@ -13,7 +13,6 @@ require (
|
||||
|
||||
require (
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/Luzifer/go-openssl/v4 v4.1.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
@ -22,6 +21,8 @@ require (
|
||||
github.com/go-openapi/strfmt v0.21.3 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mitchellh/mapstructure v1.3.3 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
@ -35,7 +36,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Luzifer/go-openssl v2.0.0+incompatible
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jedib0t/go-pretty v4.3.0+incompatible
|
||||
|
20
cli/go.sum
20
cli/go.sum
@ -2,10 +2,6 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
|
||||
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
|
||||
github.com/Luzifer/go-openssl v2.0.0+incompatible h1:EpNNxrPDji4rRzE0KeOeIeV7pHyKe8zF9oNnAXy4mBY=
|
||||
github.com/Luzifer/go-openssl v2.0.0+incompatible/go.mod h1:t2qnLjT8WQ3usGU1R8uAqjY4T7CK7eMg9vhQ3l9Ue/Y=
|
||||
github.com/Luzifer/go-openssl/v4 v4.1.0 h1:8qi3Z6f8Aflwub/Cs4FVSmKUEg/lC8GlODbR2TyZ+nM=
|
||||
github.com/Luzifer/go-openssl/v4 v4.1.0/go.mod h1:3i1T3Pe6eQK19d86WhuQzjLyMwBaNmGmt3ZceWpWVa4=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@ -26,6 +22,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
|
||||
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o=
|
||||
@ -53,6 +51,11 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
@ -100,23 +103,21 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
go.mongodb.org/mongo-driver v1.10.0 h1:UtV6N5k14upNp4LTduX0QCufG124fSu25Wz9tu94GLg=
|
||||
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -125,7 +126,6 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
136
cli/packages/api/api.go
Normal file
136
cli/packages/api/api.go
Normal file
@ -0,0 +1,136 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/config"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func CallBatchModifySecretsByWorkspaceAndEnv(httpClient *resty.Client, request BatchModifySecretsByWorkspaceAndEnvRequest) error {
|
||||
endpoint := fmt.Sprintf("%v/v2/secret/batch-modify/workspace/%v/environment/%v", config.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetBody(request).
|
||||
Patch(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallBatchCreateSecretsByWorkspaceAndEnv(httpClient *resty.Client, request BatchCreateSecretsByWorkspaceAndEnvRequest) error {
|
||||
endpoint := fmt.Sprintf("%v/v2/secret/batch-create/workspace/%v/environment/%v", config.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetBody(request).
|
||||
Post(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient *resty.Client, request BatchDeleteSecretsBySecretIdsRequest) error {
|
||||
endpoint := fmt.Sprintf("%v/v2/secret/batch/workspace/%v/environment/%v", config.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetBody(request).
|
||||
Delete(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) {
|
||||
endpoint := fmt.Sprintf("%v/v2/workspace/%v/encrypted-key", config.INFISICAL_URL, request.WorkspaceId)
|
||||
var result GetEncryptedWorkspaceKeyResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&result).
|
||||
Get(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDetailsResponse, error) {
|
||||
var tokenDetailsResponse GetServiceTokenDetailsResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&tokenDetailsResponse).
|
||||
Get(fmt.Sprintf("%v/v2/service-token", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return tokenDetailsResponse, nil
|
||||
}
|
||||
|
||||
func CallGetSecretsV2(httpClient *resty.Client, request GetEncryptedSecretsV2Request) (GetEncryptedSecretsV2Response, error) {
|
||||
var secretsResponse GetEncryptedSecretsV2Response
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&secretsResponse).
|
||||
SetQueryParam("environment", request.EnvironmentName).
|
||||
Get(fmt.Sprintf("%v/v2/secret/workspace/%v", config.INFISICAL_URL, request.WorkspaceId))
|
||||
|
||||
if err != nil {
|
||||
return GetEncryptedSecretsV2Response{}, fmt.Errorf("CallGetSecretsV2: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return GetEncryptedSecretsV2Response{}, fmt.Errorf("CallGetSecretsV2: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return secretsResponse, nil
|
||||
}
|
||||
|
||||
func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesResponse, error) {
|
||||
var workSpacesResponse GetWorkSpacesResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&workSpacesResponse).
|
||||
Get(fmt.Sprintf("%v/v1/workspace", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return GetWorkSpacesResponse{}, err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return GetWorkSpacesResponse{}, fmt.Errorf("CallGetAllWorkSpacesUserBelongsTo: Unsuccessful response: [response=%v]", response)
|
||||
}
|
||||
|
||||
return workSpacesResponse, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package models
|
||||
package api
|
||||
|
||||
import "time"
|
||||
|
||||
@ -119,14 +119,13 @@ type PullSecretsByInfisicalTokenResponse struct {
|
||||
}
|
||||
|
||||
type GetWorkSpacesResponse struct {
|
||||
Workspaces []Workspace `json:"workspaces"`
|
||||
}
|
||||
type Workspace struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Plan string `json:"plan,omitempty"`
|
||||
V int `json:"__v"`
|
||||
Organization string `json:"organization,omitempty"`
|
||||
Workspaces []struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Plan string `json:"plan,omitempty"`
|
||||
V int `json:"__v"`
|
||||
Organization string `json:"organization,omitempty"`
|
||||
} `json:"workspaces"`
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
@ -169,30 +168,68 @@ type GetEncryptedWorkspaceKeyRequest struct {
|
||||
}
|
||||
|
||||
type GetEncryptedWorkspaceKeyResponse struct {
|
||||
LatestKey struct {
|
||||
ID string `json:"_id"`
|
||||
EncryptedKey string `json:"encryptedKey"`
|
||||
Nonce string `json:"nonce"`
|
||||
Sender struct {
|
||||
ID string `json:"_id"`
|
||||
Email string `json:"email"`
|
||||
RefreshVersion int `json:"refreshVersion"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
V int `json:"__v"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
} `json:"sender"`
|
||||
Receiver string `json:"receiver"`
|
||||
Workspace string `json:"workspace"`
|
||||
V int `json:"__v"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
} `json:"latestKey"`
|
||||
ID string `json:"_id"`
|
||||
EncryptedKey string `json:"encryptedKey"`
|
||||
Nonce string `json:"nonce"`
|
||||
Sender struct {
|
||||
ID string `json:"_id"`
|
||||
Email string `json:"email"`
|
||||
RefreshVersion int `json:"refreshVersion"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
V int `json:"__v"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
} `json:"sender"`
|
||||
Receiver string `json:"receiver"`
|
||||
Workspace string `json:"workspace"`
|
||||
V int `json:"__v"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type GetSecretsByWorkspaceIdAndEnvironmentRequest struct {
|
||||
EnvironmentName string `json:"environmentName"`
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
}
|
||||
|
||||
type GetEncryptedSecretsV2Request struct {
|
||||
EnvironmentName string `json:"environmentName"`
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
}
|
||||
|
||||
type GetEncryptedSecretsV2Response []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"`
|
||||
SecretKeyHash string `json:"secretKeyHash"`
|
||||
SecretValueCiphertext string `json:"secretValueCiphertext"`
|
||||
SecretValueIV string `json:"secretValueIV"`
|
||||
SecretValueTag string `json:"secretValueTag"`
|
||||
SecretValueHash string `json:"secretValueHash"`
|
||||
SecretCommentCiphertext string `json:"secretCommentCiphertext"`
|
||||
SecretCommentIV string `json:"secretCommentIV"`
|
||||
SecretCommentTag string `json:"secretCommentTag"`
|
||||
SecretCommentHash string `json:"secretCommentHash"`
|
||||
V int `json:"__v"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
User string `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
type GetServiceTokenDetailsResponse struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Workspace string `json:"workspace"`
|
||||
Environment string `json:"environment"`
|
||||
User string `json:"user"`
|
||||
EncryptedKey string `json:"encryptedKey"`
|
||||
Iv string `json:"iv"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
@ -29,58 +29,46 @@ var exportCmd = &cobra.Command{
|
||||
DisableFlagsInUseLine: true,
|
||||
Example: "infisical export --env=prod --format=json > secrets.json",
|
||||
Args: cobra.NoArgs,
|
||||
PreRun: toggleDebug,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
toggleDebug(cmd, args)
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
envName, err := cmd.Flags().GetString("env")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the environment flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
shouldExpandSecrets, err := cmd.Flags().GetBool("expand")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the substitute flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
}
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the project id flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
format, err := cmd.Flags().GetString("format")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the format flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
envsFromApi, err := util.GetAllEnvironmentVariables(projectId, envName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(envName)
|
||||
if err != nil {
|
||||
log.Errorln("Something went wrong when pulling secrets using your Infisical token. Double check the token, project id or environment name (dev, prod, ect.)")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to fetch secrets")
|
||||
}
|
||||
|
||||
var output string
|
||||
if shouldExpandSecrets {
|
||||
substitutions := util.SubstituteSecrets(envsFromApi)
|
||||
substitutions := util.SubstituteSecrets(secrets)
|
||||
output, err = formatEnvs(substitutions, format)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
} else {
|
||||
output, err = formatEnvs(envsFromApi, format)
|
||||
output, err = formatEnvs(secrets, format)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Print(output)
|
||||
},
|
||||
}
|
||||
@ -88,7 +76,6 @@ var exportCmd = &cobra.Command{
|
||||
func init() {
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
exportCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
|
||||
exportCmd.Flags().String("projectId", "", "The project ID from which your secrets should be pulled from")
|
||||
exportCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
exportCmd.Flags().StringP("format", "f", "dotenv", "Set the format of the output file (dotenv, json, csv)")
|
||||
}
|
||||
|
@ -5,10 +5,13 @@ package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/manifoldco/promptui"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@ -21,21 +24,10 @@ var initCmd = &cobra.Command{
|
||||
DisableFlagsInUseLine: true,
|
||||
Example: "infisical init",
|
||||
Args: cobra.ExactArgs(0),
|
||||
PreRun: toggleDebug,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// check if user is logged
|
||||
hasUserLoggedInbefore, loggedInUserEmail, err := util.IsUserLoggedIn()
|
||||
if err != nil {
|
||||
log.Info("Unexpected issue occurred while checking login status. To see more details, add flag --debug")
|
||||
log.Debugln(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !hasUserLoggedInbefore {
|
||||
log.Infoln("No logged in user. To login, please run command [infisical login]")
|
||||
return
|
||||
}
|
||||
|
||||
if util.WorkspaceConfigFileExistsInCurrentPath() {
|
||||
shouldOverride, err := shouldOverrideWorkspacePrompt()
|
||||
if err != nil {
|
||||
@ -49,23 +41,22 @@ var initCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
userCreds, err := util.GetUserCredsFromKeyRing(loggedInUserEmail)
|
||||
userCreds, err := util.GetCurrentLoggedInUserDetails()
|
||||
if err != nil {
|
||||
log.Infoln("Unable to get user creds from key ring")
|
||||
log.Debug(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to get your login details")
|
||||
}
|
||||
|
||||
workspaces, err := util.GetWorkSpacesFromAPI(userCreds)
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(userCreds.UserCredentials.JTWToken)
|
||||
workspaceResponse, err := api.CallGetAllWorkSpacesUserBelongsTo(httpClient)
|
||||
if err != nil {
|
||||
log.Errorln("Unable to pull your projects. To see more logs add the --debug flag to this command")
|
||||
log.Debugln("Unable to get your projects because:", err)
|
||||
return
|
||||
util.HandleError(err, "Unable to pull projects that belong to you")
|
||||
}
|
||||
|
||||
workspaces := workspaceResponse.Workspaces
|
||||
if len(workspaces) == 0 {
|
||||
log.Infoln("You don't have any projects created in Infisical. You must first create a project at https://infisical.com")
|
||||
return
|
||||
message := fmt.Sprintf("You don't have any projects created in Infisical. You must first create a project at %s", util.INFISICAL_TOKEN_NAME)
|
||||
util.PrintMessageAndExit(message)
|
||||
}
|
||||
|
||||
var workspaceNames []string
|
||||
@ -81,16 +72,12 @@ var initCmd = &cobra.Command{
|
||||
|
||||
index, _, err := prompt.Run()
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse your response")
|
||||
log.Debug(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
err = writeWorkspaceFile(workspaces[index])
|
||||
if err != nil {
|
||||
log.Errorln("Something went wrong when creating your workspace file")
|
||||
log.Debug("Error while writing your workspace file:", err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/config"
|
||||
"github.com/Infisical/infisical-merge/packages/crypto"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/Infisical/infisical-merge/packages/srp"
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
@ -27,18 +30,15 @@ var loginCmd = &cobra.Command{
|
||||
DisableFlagsInUseLine: true,
|
||||
PreRun: toggleDebug,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
hasUserLoggedInbefore, currentLoggedInUserEmail, err := util.IsUserLoggedIn()
|
||||
|
||||
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
||||
if err != nil {
|
||||
log.Debugln("Unable to get current logged in user.", err)
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if hasUserLoggedInbefore {
|
||||
shouldOverride, err := shouldOverrideLoginPrompt(currentLoggedInUserEmail)
|
||||
if currentLoggedInUserDetails.IsUserLoggedIn {
|
||||
shouldOverride, err := shouldOverrideLoginPrompt(currentLoggedInUserDetails.UserCredentials.Email)
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse your answer")
|
||||
log.Debug(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if !shouldOverride {
|
||||
@ -48,14 +48,12 @@ var loginCmd = &cobra.Command{
|
||||
|
||||
email, password, err := askForLoginCredentials()
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse email and password for authentication")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to parse email and password for authentication")
|
||||
}
|
||||
|
||||
userCredentials, err := getFreshUserCredentials(email, password)
|
||||
if err != nil {
|
||||
log.Errorln("Unable to authenticate with the provided credentials, please try again")
|
||||
log.Infoln("Unable to authenticate with the provided credentials, please try again")
|
||||
log.Debugln(err)
|
||||
return
|
||||
}
|
||||
@ -63,24 +61,20 @@ var loginCmd = &cobra.Command{
|
||||
encryptedPrivateKey, _ := base64.StdEncoding.DecodeString(userCredentials.EncryptedPrivateKey)
|
||||
tag, err := base64.StdEncoding.DecodeString(userCredentials.Tag)
|
||||
if err != nil {
|
||||
log.Errorln("Unable to decode the auth tag")
|
||||
log.Debugln(err)
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
IV, err := base64.StdEncoding.DecodeString(userCredentials.IV)
|
||||
if err != nil {
|
||||
log.Errorln("Unable to decode the IV/Nonce")
|
||||
log.Debugln(err)
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
paddedPassword := fmt.Sprintf("%032s", password)
|
||||
key := []byte(paddedPassword)
|
||||
|
||||
decryptedPrivateKey, err := util.DecryptSymmetric(key, encryptedPrivateKey, tag, IV)
|
||||
decryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV)
|
||||
if err != nil || len(decryptedPrivateKey) == 0 {
|
||||
log.Errorln("There was an issue decrypting your keys")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
userCredentialsToBeStored := &models.UserCredentials{
|
||||
@ -100,9 +94,7 @@ var loginCmd = &cobra.Command{
|
||||
|
||||
err = util.WriteInitalConfig(userCredentialsToBeStored)
|
||||
if err != nil {
|
||||
log.Errorln("Unable to write write to Infisical Config file. Please try again")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to write write to Infisical Config file. Please try again")
|
||||
}
|
||||
|
||||
log.Infoln("Nice! You are loggin as:", email)
|
||||
@ -156,7 +148,7 @@ func askForLoginCredentials() (email string, password string, err error) {
|
||||
return userEmail, userPassword, nil
|
||||
}
|
||||
|
||||
func getFreshUserCredentials(email string, password string) (*models.LoginTwoResponse, error) {
|
||||
func getFreshUserCredentials(email string, password string) (*api.LoginTwoResponse, error) {
|
||||
log.Debugln("getFreshUserCredentials:", "email", email, "password", password)
|
||||
httpClient := resty.New()
|
||||
httpClient.SetRetryCount(5)
|
||||
@ -167,18 +159,18 @@ func getFreshUserCredentials(email string, password string) (*models.LoginTwoRes
|
||||
srpA := hex.EncodeToString(srpClient.ComputeA())
|
||||
|
||||
// ** Login one
|
||||
loginOneRequest := models.LoginOneRequest{
|
||||
loginOneRequest := api.LoginOneRequest{
|
||||
Email: email,
|
||||
ClientPublicKey: srpA,
|
||||
}
|
||||
|
||||
var loginOneResponseResult models.LoginOneResponse
|
||||
var loginOneResponseResult api.LoginOneResponse
|
||||
|
||||
loginOneResponse, err := httpClient.
|
||||
R().
|
||||
SetBody(loginOneRequest).
|
||||
SetResult(&loginOneResponseResult).
|
||||
Post(fmt.Sprintf("%v/v1/auth/login1", util.INFISICAL_URL))
|
||||
Post(fmt.Sprintf("%v/v1/auth/login1", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -204,17 +196,17 @@ func getFreshUserCredentials(email string, password string) (*models.LoginTwoRes
|
||||
|
||||
srpM1 := srpClient.ComputeM1()
|
||||
|
||||
LoginTwoRequest := models.LoginTwoRequest{
|
||||
LoginTwoRequest := api.LoginTwoRequest{
|
||||
Email: email,
|
||||
ClientProof: hex.EncodeToString(srpM1),
|
||||
}
|
||||
|
||||
var loginTwoResponseResult models.LoginTwoResponse
|
||||
var loginTwoResponseResult api.LoginTwoResponse
|
||||
loginTwoResponse, err := httpClient.
|
||||
R().
|
||||
SetBody(LoginTwoRequest).
|
||||
SetResult(&loginTwoResponseResult).
|
||||
Post(fmt.Sprintf("%v/v1/auth/login2", util.INFISICAL_URL))
|
||||
Post(fmt.Sprintf("%v/v1/auth/login2", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6,7 +6,7 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
"github.com/Infisical/infisical-merge/packages/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -30,7 +30,7 @@ func Execute() {
|
||||
func init() {
|
||||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
rootCmd.PersistentFlags().BoolVarP(&debugLogging, "debug", "d", false, "Enable verbose logging")
|
||||
rootCmd.PersistentFlags().StringVar(&util.INFISICAL_URL, "domain", "https://app.infisical.com/api", "Point the CLI to your own backend")
|
||||
rootCmd.PersistentFlags().StringVar(&config.INFISICAL_URL, "domain", "https://app.infisical.com/api", "Point the CLI to your own backend")
|
||||
// rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
// }
|
||||
}
|
||||
|
@ -55,36 +55,26 @@ var runCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
envName, err := cmd.Flags().GetString("env")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the environment flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
if !util.IsSecretEnvironmentValid(envName) {
|
||||
util.PrintMessageAndExit("Invalid environment name passed. Environment names can only be prod, dev, test or staging")
|
||||
}
|
||||
|
||||
secretOverriding, err := cmd.Flags().GetBool("secret-overriding")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the secret-overriding flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
shouldExpandSecrets, err := cmd.Flags().GetBool("expand")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the substitute flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
projectId, err := cmd.Flags().GetString("projectId")
|
||||
secrets, err := util.GetAllEnvironmentVariables(envName)
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the project id flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(projectId, envName)
|
||||
if err != nil {
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid")
|
||||
}
|
||||
|
||||
if shouldExpandSecrets {
|
||||
@ -97,29 +87,26 @@ var runCmd = &cobra.Command{
|
||||
|
||||
if cmd.Flags().Changed("command") {
|
||||
command := cmd.Flag("command").Value.String()
|
||||
|
||||
err = executeMultipleCommandWithEnvs(command, secrets)
|
||||
if err != nil {
|
||||
log.Errorf("Something went wrong when executing your command [error=%s]", err)
|
||||
return
|
||||
util.HandleError(err, "Unable to execute your chained command")
|
||||
}
|
||||
|
||||
} else {
|
||||
err = executeSingleCommandWithEnvs(args, secrets)
|
||||
if err != nil {
|
||||
log.Errorf("Something went wrong when executing your command [error=%s]", err)
|
||||
return
|
||||
util.HandleError(err, "Unable to execute your single command")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(runCmd)
|
||||
runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
|
||||
runCmd.Flags().String("projectId", "", "The project ID from which your secrets should be pulled from")
|
||||
runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
runCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets with the same name over shared secrets")
|
||||
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 ...\")")
|
||||
}
|
||||
|
||||
@ -130,7 +117,7 @@ func executeSingleCommandWithEnvs(args []string, secrets []models.SingleEnvironm
|
||||
numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets))
|
||||
log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected)
|
||||
log.Debugf("executing command: %s %s \n", command, strings.Join(argsForCommand, " "))
|
||||
log.Debugln("Secrets injected:", secrets)
|
||||
log.Debugf("Secrets injected: %v", secrets)
|
||||
|
||||
cmd := exec.Command(command, argsForCommand...)
|
||||
cmd.Stdin = os.Stdin
|
||||
@ -158,7 +145,7 @@ func executeMultipleCommandWithEnvs(fullCommand string, secrets []models.SingleE
|
||||
numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets))
|
||||
log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected)
|
||||
log.Debugf("executing command: %s %s %s \n", shell[0], shell[1], fullCommand)
|
||||
log.Debugln("Secrets injected:", secrets)
|
||||
log.Debugf("Secrets injected: %v", secrets)
|
||||
|
||||
return execCmd(cmd)
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ import (
|
||||
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/http"
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/crypto"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
"github.com/Infisical/infisical-merge/packages/visualize"
|
||||
@ -30,35 +31,23 @@ var secretsCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
environmentName, err := cmd.Flags().GetString("env")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the environment name flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
shouldExpandSecrets, err := cmd.Flags().GetBool("expand")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the substitute flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
workspaceFileExists := util.WorkspaceConfigFileExistsInCurrentPath()
|
||||
if !workspaceFileExists {
|
||||
log.Error("You have not yet connected to an Infisical Project. Please run [infisical init]")
|
||||
return
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables("", environmentName)
|
||||
|
||||
if shouldExpandSecrets {
|
||||
secrets = util.SubstituteSecrets(secrets)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Debugln(err)
|
||||
return
|
||||
}
|
||||
|
||||
visualize.PrintAllSecretDetails(secrets)
|
||||
},
|
||||
}
|
||||
@ -81,85 +70,50 @@ var secretsSetCmd = &cobra.Command{
|
||||
PreRun: toggleDebug,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// secretType, err := cmd.Flags().GetString("type")
|
||||
// if err != nil {
|
||||
// log.Errorln("Unable to parse the secret type flag")
|
||||
// log.Debugln(err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// if !util.IsSecretTypeValid(secretType) {
|
||||
// log.Errorf("secret type can only be `personal` or `shared`. You have entered [%v]", secretType)
|
||||
// return
|
||||
// }
|
||||
|
||||
environmentName, err := cmd.Flags().GetString("env")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the environment name flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
if !util.IsSecretEnvironmentValid(environmentName) {
|
||||
log.Errorln("You have entered a invalid environment name. Environment names can only be prod, dev, test or staging")
|
||||
return
|
||||
}
|
||||
|
||||
workspaceFileExists := util.WorkspaceConfigFileExistsInCurrentPath()
|
||||
if !workspaceFileExists {
|
||||
log.Error("You have not yet connected to an Infisical Project. Please run [infisical init]")
|
||||
return
|
||||
util.PrintMessageAndExit("You have entered a invalid environment name", "Environment names can only be prod, dev, test or staging")
|
||||
}
|
||||
|
||||
workspaceFile, err := util.GetWorkSpaceFromFile()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to get your local config details")
|
||||
}
|
||||
|
||||
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !loggedInUserDetails.IsUserLoggedIn {
|
||||
log.Error("You are not logged in yet. Please run [infisical login] then try again")
|
||||
return
|
||||
}
|
||||
|
||||
if loggedInUserDetails.IsUserLoggedIn && loggedInUserDetails.LoginExpired {
|
||||
log.Error("Your login has expired. Please run [infisical login] then try again")
|
||||
return
|
||||
util.HandleError(err, "Unable to authenticate")
|
||||
}
|
||||
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
request := models.GetEncryptedWorkspaceKeyRequest{
|
||||
request := api.GetEncryptedWorkspaceKeyRequest{
|
||||
WorkspaceId: workspaceFile.WorkspaceId,
|
||||
}
|
||||
|
||||
workspaceKeyResponse, err := http.CallGetEncryptedWorkspaceKey(httpClient, request)
|
||||
workspaceKeyResponse, err := api.CallGetEncryptedWorkspaceKey(httpClient, request)
|
||||
if err != nil {
|
||||
log.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
|
||||
return
|
||||
util.HandleError(err, "unable to get your encrypted workspace key")
|
||||
}
|
||||
|
||||
encryptedWorkspaceKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.LatestKey.EncryptedKey)
|
||||
encryptedWorkspaceKeySenderPublicKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.LatestKey.Sender.PublicKey)
|
||||
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.LatestKey.Nonce)
|
||||
encryptedWorkspaceKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey)
|
||||
encryptedWorkspaceKeySenderPublicKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Sender.PublicKey)
|
||||
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce)
|
||||
currentUsersPrivateKey, _ := base64.StdEncoding.DecodeString(loggedInUserDetails.UserCredentials.PrivateKey)
|
||||
|
||||
// decrypt workspace key
|
||||
plainTextEncryptionKey := util.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||
|
||||
// pull current secrets
|
||||
secrets, err := util.GetAllEnvironmentVariables("", environmentName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
if err != nil {
|
||||
log.Error("unable to retrieve secrets. Run with -d to see full logs")
|
||||
log.Debug(err)
|
||||
util.HandleError(err, "unable to retrieve secrets")
|
||||
}
|
||||
|
||||
type SecretSetOperation struct {
|
||||
@ -168,8 +122,8 @@ var secretsSetCmd = &cobra.Command{
|
||||
SecretOperation string
|
||||
}
|
||||
|
||||
secretsToCreate := []models.Secret{}
|
||||
secretsToModify := []models.Secret{}
|
||||
secretsToCreate := []api.Secret{}
|
||||
secretsToModify := []api.Secret{}
|
||||
secretOperations := []SecretSetOperation{}
|
||||
|
||||
secretByKey := getSecretsByKeys(secrets)
|
||||
@ -177,13 +131,11 @@ var secretsSetCmd = &cobra.Command{
|
||||
for _, arg := range args {
|
||||
splitKeyValueFromArg := strings.SplitN(arg, "=", 2)
|
||||
if splitKeyValueFromArg[0] == "" || splitKeyValueFromArg[1] == "" {
|
||||
log.Error("ensure that each secret has a none empty key and value. Modify the input and try again")
|
||||
return
|
||||
util.PrintMessageAndExit("ensure that each secret has a none empty key and value. Modify the input and try again")
|
||||
}
|
||||
|
||||
if unicode.IsNumber(rune(splitKeyValueFromArg[0][0])) {
|
||||
log.Error("keys of secrets cannot start with a number. Modify the key name(s) and try again")
|
||||
return
|
||||
util.PrintMessageAndExit("keys of secrets cannot start with a number. Modify the key name(s) and try again")
|
||||
}
|
||||
|
||||
// Key and value from argument
|
||||
@ -191,20 +143,20 @@ var secretsSetCmd = &cobra.Command{
|
||||
value := splitKeyValueFromArg[1]
|
||||
|
||||
hashedKey := fmt.Sprintf("%x", sha256.Sum256([]byte(key)))
|
||||
encryptedKey, err := util.EncryptSymmetric([]byte(key), []byte(plainTextEncryptionKey))
|
||||
encryptedKey, err := crypto.EncryptSymmetric([]byte(key), []byte(plainTextEncryptionKey))
|
||||
if err != nil {
|
||||
log.Errorf("unable to encrypt your secrets [err=%v]", err)
|
||||
util.HandleError(err, "unable to encrypt your secrets")
|
||||
}
|
||||
|
||||
hashedValue := fmt.Sprintf("%x", sha256.Sum256([]byte(value)))
|
||||
encryptedValue, err := util.EncryptSymmetric([]byte(value), []byte(plainTextEncryptionKey))
|
||||
encryptedValue, err := crypto.EncryptSymmetric([]byte(value), []byte(plainTextEncryptionKey))
|
||||
if err != nil {
|
||||
log.Errorf("unable to encrypt your secrets [err=%v]", err)
|
||||
util.HandleError(err, "unable to encrypt your secrets")
|
||||
}
|
||||
|
||||
if existingSecret, ok := secretByKey[key]; ok {
|
||||
// case: secret exists in project so it needs to be modified
|
||||
encryptedSecretDetails := models.Secret{
|
||||
encryptedSecretDetails := api.Secret{
|
||||
ID: existingSecret.ID,
|
||||
SecretValueCiphertext: base64.StdEncoding.EncodeToString(encryptedValue.CipherText),
|
||||
SecretValueIV: base64.StdEncoding.EncodeToString(encryptedValue.Nonce),
|
||||
@ -231,7 +183,7 @@ var secretsSetCmd = &cobra.Command{
|
||||
|
||||
} else {
|
||||
// case: secret doesn't exist in project so it needs to be created
|
||||
encryptedSecretDetails := models.Secret{
|
||||
encryptedSecretDetails := api.Secret{
|
||||
SecretKeyCiphertext: base64.StdEncoding.EncodeToString(encryptedKey.CipherText),
|
||||
SecretKeyIV: base64.StdEncoding.EncodeToString(encryptedKey.Nonce),
|
||||
SecretKeyTag: base64.StdEncoding.EncodeToString(encryptedKey.AuthTag),
|
||||
@ -252,29 +204,29 @@ var secretsSetCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if len(secretsToCreate) > 0 {
|
||||
batchCreateRequest := models.BatchCreateSecretsByWorkspaceAndEnvRequest{
|
||||
batchCreateRequest := api.BatchCreateSecretsByWorkspaceAndEnvRequest{
|
||||
WorkspaceId: workspaceFile.WorkspaceId,
|
||||
EnvironmentName: environmentName,
|
||||
Secrets: secretsToCreate,
|
||||
}
|
||||
|
||||
err = http.CallBatchCreateSecretsByWorkspaceAndEnv(httpClient, batchCreateRequest)
|
||||
err = api.CallBatchCreateSecretsByWorkspaceAndEnv(httpClient, batchCreateRequest)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to process new secret creations because %v", err)
|
||||
util.HandleError(err, "Unable to process new secret creations")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(secretsToModify) > 0 {
|
||||
batchModifyRequest := models.BatchModifySecretsByWorkspaceAndEnvRequest{
|
||||
batchModifyRequest := api.BatchModifySecretsByWorkspaceAndEnvRequest{
|
||||
WorkspaceId: workspaceFile.WorkspaceId,
|
||||
EnvironmentName: environmentName,
|
||||
Secrets: secretsToModify,
|
||||
}
|
||||
|
||||
err = http.CallBatchModifySecretsByWorkspaceAndEnv(httpClient, batchModifyRequest)
|
||||
err = api.CallBatchModifySecretsByWorkspaceAndEnv(httpClient, batchModifyRequest)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to process the modifications to your secrets because %v", err)
|
||||
util.HandleError(err, "Unable to process the modifications to your secrets")
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -307,36 +259,17 @@ var secretsDeleteCmd = &cobra.Command{
|
||||
|
||||
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !loggedInUserDetails.IsUserLoggedIn {
|
||||
log.Error("You are not logged in yet. Please run [infisical login] then try again")
|
||||
return
|
||||
}
|
||||
|
||||
if loggedInUserDetails.IsUserLoggedIn && loggedInUserDetails.LoginExpired {
|
||||
log.Error("Your login has expired. Please run [infisical login] then try again")
|
||||
return
|
||||
}
|
||||
|
||||
workspaceFileExists := util.WorkspaceConfigFileExistsInCurrentPath()
|
||||
if !workspaceFileExists {
|
||||
log.Error("You have not yet connected to an Infisical Project. Please run [infisical init]")
|
||||
return
|
||||
util.HandleError(err, "Unable to authenticate")
|
||||
}
|
||||
|
||||
workspaceFile, err := util.GetWorkSpaceFromFile()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to get local project details")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables("", environmentName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
if err != nil {
|
||||
log.Error("Unable to retrieve secrets. Run with -d to see full logs")
|
||||
log.Debug(err)
|
||||
util.HandleError(err, "Unable to fetch secrets")
|
||||
}
|
||||
|
||||
secretByKey := getSecretsByKeys(secrets)
|
||||
@ -352,11 +285,11 @@ var secretsDeleteCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if len(invalidSecretNamesThatDoNotExist) != 0 {
|
||||
log.Errorf("secret name(s) [%v] does not exist in your project. To see which secrets exist run [infisical secrets]", strings.Join(invalidSecretNamesThatDoNotExist, ", "))
|
||||
return
|
||||
message := fmt.Sprintf("secret name(s) [%v] does not exist in your project. To see which secrets exist run [infisical secrets]", strings.Join(invalidSecretNamesThatDoNotExist, ", "))
|
||||
util.PrintMessageAndExit(message)
|
||||
}
|
||||
|
||||
request := models.BatchDeleteSecretsBySecretIdsRequest{
|
||||
request := api.BatchDeleteSecretsBySecretIdsRequest{
|
||||
WorkspaceId: workspaceFile.WorkspaceId,
|
||||
EnvironmentName: environmentName,
|
||||
SecretIds: validSecretIdsToDelete,
|
||||
@ -366,45 +299,43 @@ var secretsDeleteCmd = &cobra.Command{
|
||||
SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
err = http.CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient, request)
|
||||
err = api.CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient, request)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to complete your request because %v", err)
|
||||
return
|
||||
util.HandleError(err, "Unable to complete your batch delete request")
|
||||
}
|
||||
|
||||
log.Infof("secret name(s) [%v] have been deleted from your project", strings.Join(args, ", "))
|
||||
fmt.Printf("secret name(s) [%v] have been deleted from your project \n", strings.Join(args, ", "))
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
secretsCmd.AddCommand(secretsGetCmd)
|
||||
// secretsSetCmd.Flags().String("type", "shared", "Used to set the type for secrets")
|
||||
secretsCmd.AddCommand(secretsSetCmd)
|
||||
secretsCmd.AddCommand(secretsDeleteCmd)
|
||||
secretsCmd.PersistentFlags().String("env", "dev", "Used to define the environment name on which actions should be taken on")
|
||||
secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
secretsCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
}
|
||||
rootCmd.AddCommand(secretsCmd)
|
||||
}
|
||||
|
||||
func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
environmentName, err := cmd.Flags().GetString("env")
|
||||
if err != nil {
|
||||
log.Errorln("Unable to parse the environment name flag")
|
||||
log.Debugln(err)
|
||||
return
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
workspaceFileExists := util.WorkspaceConfigFileExistsInCurrentPath()
|
||||
if !workspaceFileExists {
|
||||
log.Error("You have not yet connected to an Infisical Project. Please run [infisical init]")
|
||||
return
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables("", environmentName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
if err != nil {
|
||||
log.Error("Unable to retrieve secrets. Run with -d to see full logs")
|
||||
log.Debug(err)
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
|
||||
requestedSecrets := []models.SingleEnvironmentVariable{}
|
||||
|
3
cli/packages/config/config.go
Normal file
3
cli/packages/config/config.go
Normal file
@ -0,0 +1,3 @@
|
||||
package config
|
||||
|
||||
var INFISICAL_URL = "http://localhost:8080/api"
|
@ -1,4 +1,4 @@
|
||||
package util
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
@ -1,102 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
func CallBatchModifySecretsByWorkspaceAndEnv(httpClient *resty.Client, request models.BatchModifySecretsByWorkspaceAndEnvRequest) error {
|
||||
endpoint := fmt.Sprintf("%v/v2/secret/batch-modify/workspace/%v/environment/%v", util.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetBody(request).
|
||||
Patch(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return fmt.Errorf("CallBatchModifySecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallBatchCreateSecretsByWorkspaceAndEnv(httpClient *resty.Client, request models.BatchCreateSecretsByWorkspaceAndEnvRequest) error {
|
||||
endpoint := fmt.Sprintf("%v/v2/secret/batch-create/workspace/%v/environment/%v", util.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetBody(request).
|
||||
Post(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return fmt.Errorf("CallBatchCreateSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallBatchDeleteSecretsByWorkspaceAndEnv(httpClient *resty.Client, request models.BatchDeleteSecretsBySecretIdsRequest) error {
|
||||
endpoint := fmt.Sprintf("%v/v2/secret/batch/workspace/%v/environment/%v", util.INFISICAL_URL, request.WorkspaceId, request.EnvironmentName)
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetBody(request).
|
||||
Delete(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return fmt.Errorf("CallBatchDeleteSecretsByWorkspaceAndEnv: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request models.GetEncryptedWorkspaceKeyRequest) (models.GetEncryptedWorkspaceKeyResponse, error) {
|
||||
endpoint := fmt.Sprintf("%v/v1/key/%v/latest", util.INFISICAL_URL, request.WorkspaceId)
|
||||
var result models.GetEncryptedWorkspaceKeyResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&result).
|
||||
Get(endpoint)
|
||||
|
||||
if err != nil {
|
||||
return models.GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return models.GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func CallGetEncryptedSecretsByWorkspaceIdAndEnv(httpClient resty.Client, request models.GetSecretsByWorkspaceIdAndEnvironmentRequest) (models.PullSecretsResponse, error) {
|
||||
var pullSecretsRequestResponse models.PullSecretsResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetQueryParam("environment", request.EnvironmentName).
|
||||
SetQueryParam("channel", "cli").
|
||||
SetResult(&pullSecretsRequestResponse).
|
||||
Get(fmt.Sprintf("%v/v1/secret/%v", util.INFISICAL_URL, request.WorkspaceId))
|
||||
|
||||
if err != nil {
|
||||
return models.PullSecretsResponse{}, fmt.Errorf("CallGetEncryptedSecretsByWorkspaceIdAndEnv: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return models.PullSecretsResponse{}, fmt.Errorf("CallGetEncryptedSecretsByWorkspaceIdAndEnv: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return pullSecretsRequestResponse, nil
|
||||
}
|
@ -21,6 +21,14 @@ type SingleEnvironmentVariable struct {
|
||||
ID string `json:"_id"`
|
||||
}
|
||||
|
||||
type Workspace struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Plan string `json:"plan,omitempty"`
|
||||
V int `json:"__v"`
|
||||
Organization string `json:"organization,omitempty"`
|
||||
}
|
||||
|
||||
type WorkspaceConfigFile struct {
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
}
|
||||
|
@ -5,17 +5,6 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
CONFIG_FILE_NAME = "infisical-config.json"
|
||||
CONFIG_FOLDER_NAME = ".infisical"
|
||||
INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
|
||||
INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
|
||||
SECRET_TYPE_PERSONAL = "personal"
|
||||
SECRET_TYPE_SHARED = "shared"
|
||||
)
|
||||
|
||||
var INFISICAL_URL = "https://app.infisical.com/api"
|
||||
|
||||
func GetHomeDir() (string, error) {
|
||||
directory, err := os.UserHomeDir()
|
||||
return directory, err
|
||||
@ -25,7 +14,7 @@ func GetHomeDir() (string, error) {
|
||||
func WriteToFile(fileName string, dataToWrite []byte, filePerm os.FileMode) error {
|
||||
err := os.WriteFile(fileName, dataToWrite, filePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to wrote to file", err)
|
||||
return fmt.Errorf("unable to wrote to file [err=%v]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
13
cli/packages/util/constants.go
Normal file
13
cli/packages/util/constants.go
Normal file
@ -0,0 +1,13 @@
|
||||
package util
|
||||
|
||||
const (
|
||||
CONFIG_FILE_NAME = "infisical-config.json"
|
||||
CONFIG_FOLDER_NAME = ".infisical"
|
||||
INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
|
||||
INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
|
||||
SECRET_TYPE_PERSONAL = "personal"
|
||||
SECRET_TYPE_SHARED = "shared"
|
||||
KEYRING_SERVICE_NAME = "infisical"
|
||||
PERSONAL_SECRET_TYPE_NAME = "personal"
|
||||
SHARED_SECRET_TYPE_NAME = "shared"
|
||||
)
|
@ -5,20 +5,17 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/99designs/keyring"
|
||||
"github.com/Infisical/infisical-merge/packages/config"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const SERVICE_NAME = "infisical"
|
||||
|
||||
type LoggedInUserDetails struct {
|
||||
IsUserLoggedIn bool
|
||||
LoginExpired bool
|
||||
UserCredentials models.UserCredentials
|
||||
}
|
||||
|
||||
// To do: what happens if the user doesn't have a keyring in their system?
|
||||
func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error {
|
||||
userCredMarshalled, err := json.Marshal(userCred)
|
||||
if err != nil {
|
||||
@ -69,46 +66,6 @@ func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentia
|
||||
return userCredentials, err
|
||||
}
|
||||
|
||||
func IsUserLoggedIn() (hasUserLoggedIn bool, theUsersEmail string, err error) {
|
||||
if ConfigFileExists() {
|
||||
configFile, err := GetConfigFile()
|
||||
if err != nil {
|
||||
return false, "", fmt.Errorf("IsUserLoggedIn: unable to get logged in user from config file [err=%s]", err)
|
||||
}
|
||||
|
||||
if configFile.LoggedInUserEmail == "" {
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
userCreds, err := GetUserCredsFromKeyRing(configFile.LoggedInUserEmail)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
// check to to see if the JWT is still valid
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(userCreds.JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
response, err := httpClient.
|
||||
R().
|
||||
Post(fmt.Sprintf("%v/v1/auth/checkAuth", INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
log.Infoln("Login expired, please login again.")
|
||||
return false, "", fmt.Errorf("GetUserCredsFromKeyRing: Login expired, please login again.")
|
||||
}
|
||||
|
||||
return true, configFile.LoggedInUserEmail, nil
|
||||
} else {
|
||||
return false, "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
|
||||
if ConfigFileExists() {
|
||||
configFile, err := GetConfigFile()
|
||||
@ -132,7 +89,7 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
|
||||
|
||||
response, err := httpClient.
|
||||
R().
|
||||
Post(fmt.Sprintf("%v/v1/auth/checkAuth", INFISICAL_URL))
|
||||
Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return LoggedInUserDetails{}, err
|
||||
|
38
cli/packages/util/errors.go
Normal file
38
cli/packages/util/errors.go
Normal file
@ -0,0 +1,38 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
func HandleError(err error, messages ...string) {
|
||||
PrintErrorAndExit(1, err, messages...)
|
||||
}
|
||||
|
||||
func PrintErrorAndExit(exitCode int, err error, messages ...string) {
|
||||
printError(err)
|
||||
|
||||
if len(messages) > 0 {
|
||||
for _, message := range messages {
|
||||
fmt.Println(message)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func PrintMessageAndExit(messages ...string) {
|
||||
if len(messages) > 0 {
|
||||
for _, message := range messages {
|
||||
fmt.Println(message)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func printError(e error) {
|
||||
color.Red("Hmm, we ran into an error: %v", e)
|
||||
}
|
100
cli/packages/util/helper.go
Normal file
100
cli/packages/util/helper.go
Normal file
@ -0,0 +1,100 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type DecodedSymmetricEncryptionDetails = struct {
|
||||
Cipher []byte
|
||||
IV []byte
|
||||
Tag []byte
|
||||
Key []byte
|
||||
}
|
||||
|
||||
func GetBase64DecodedSymmetricEncryptionDetails(key string, cipher string, IV string, tag string) (DecodedSymmetricEncryptionDetails, error) {
|
||||
cipherx, err := base64.StdEncoding.DecodeString(cipher)
|
||||
if err != nil {
|
||||
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode cipher text [err=%v]", err)
|
||||
}
|
||||
|
||||
keyx, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil {
|
||||
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode key [err=%v]", err)
|
||||
}
|
||||
|
||||
IVx, err := base64.StdEncoding.DecodeString(IV)
|
||||
if err != nil {
|
||||
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode IV [err=%v]", err)
|
||||
}
|
||||
|
||||
tagx, err := base64.StdEncoding.DecodeString(tag)
|
||||
if err != nil {
|
||||
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode tag [err=%v]", err)
|
||||
}
|
||||
|
||||
return DecodedSymmetricEncryptionDetails{
|
||||
Key: keyx,
|
||||
Cipher: cipherx,
|
||||
IV: IVx,
|
||||
Tag: tagx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func IsSecretEnvironmentValid(env string) bool {
|
||||
if env == "prod" || env == "dev" || env == "test" || env == "staging" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsSecretTypeValid(s string) bool {
|
||||
if s == "personal" || s == "shared" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RequireLogin() {
|
||||
currentUserDetails, err := GetCurrentLoggedInUserDetails()
|
||||
|
||||
if err != nil {
|
||||
HandleError(err, "unable to retrieve your login details")
|
||||
}
|
||||
|
||||
if !currentUserDetails.IsUserLoggedIn {
|
||||
PrintMessageAndExit("You must be logged in to run this command. To login, run [infisical login]")
|
||||
}
|
||||
|
||||
if currentUserDetails.LoginExpired {
|
||||
PrintMessageAndExit("Your login expired, please login in again. To login, run [infisical login]")
|
||||
}
|
||||
|
||||
if currentUserDetails.UserCredentials.Email == "" && currentUserDetails.UserCredentials.JTWToken == "" && currentUserDetails.UserCredentials.PrivateKey == "" {
|
||||
PrintMessageAndExit("One or more of your login details is empty. Please try logging in again via by running [infisical login]")
|
||||
}
|
||||
}
|
||||
|
||||
func RequireServiceToken() {
|
||||
serviceToken := os.Getenv(INFISICAL_TOKEN_NAME)
|
||||
if serviceToken == "" {
|
||||
PrintMessageAndExit("No service token is found in your terminal")
|
||||
}
|
||||
}
|
||||
|
||||
func RequireLocalWorkspaceFile() {
|
||||
workspaceFileExists := WorkspaceConfigFileExistsInCurrentPath()
|
||||
if !workspaceFileExists {
|
||||
PrintMessageAndExit("It looks you have not yet connected this project to Infisical", "To do so, run [infisical init] then run your command again")
|
||||
}
|
||||
|
||||
workspaceFile, err := GetWorkSpaceFromFile()
|
||||
if err != nil {
|
||||
HandleError(err, "Unable to read your project configuration, please try initializing this project again.", "Run [infisical init]")
|
||||
}
|
||||
|
||||
if workspaceFile.WorkspaceId == "" {
|
||||
PrintMessageAndExit("Your project id is missing in your local config file. Please add it or run again [infisical init]")
|
||||
}
|
||||
}
|
@ -2,284 +2,127 @@ package util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/crypto"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
const PERSONAL_SECRET_TYPE_NAME = "personal"
|
||||
const SHARED_SECRET_TYPE_NAME = "shared"
|
||||
|
||||
func getSecretsByWorkspaceIdAndEnvName(httpClient resty.Client, envName string, workspace models.WorkspaceConfigFile, userCreds models.UserCredentials) (listOfSecrets []models.SingleEnvironmentVariable, err error) {
|
||||
var pullSecretsRequestResponse models.PullSecretsResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetQueryParam("environment", envName).
|
||||
SetQueryParam("channel", "cli").
|
||||
SetResult(&pullSecretsRequestResponse).
|
||||
Get(fmt.Sprintf("%v/v1/secret/%v", INFISICAL_URL, workspace.WorkspaceId)) // need to change workspace id
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.SingleEnvironmentVariable, 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")
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return nil, fmt.Errorf(response.Status())
|
||||
}
|
||||
serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])
|
||||
|
||||
// Get workspace key
|
||||
workspaceKey, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.EncryptedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.Nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
senderPublicKey, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.Sender.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(userCreds.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// log.Debugln("workspaceKey", workspaceKey, "nonce", nonce, "senderPublicKey", senderPublicKey, "currentUsersPrivateKey", currentUsersPrivateKey)
|
||||
workspaceKeyInBytes := DecryptAsymmetric(workspaceKey, nonce, senderPublicKey, currentUsersPrivateKey)
|
||||
var listOfEnv []models.SingleEnvironmentVariable
|
||||
|
||||
for _, secret := range pullSecretsRequestResponse.Secrets {
|
||||
key_iv, _ := base64.StdEncoding.DecodeString(secret.SecretKeyIV)
|
||||
key_tag, _ := base64.StdEncoding.DecodeString(secret.SecretKeyTag)
|
||||
key_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretKeyCiphertext)
|
||||
|
||||
plainTextKey, err := DecryptSymmetric(workspaceKeyInBytes, key_ciphertext, key_tag, key_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value_iv, _ := base64.StdEncoding.DecodeString(secret.SecretValueIV)
|
||||
value_tag, _ := base64.StdEncoding.DecodeString(secret.SecretValueTag)
|
||||
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValueCiphertext)
|
||||
|
||||
plainTextValue, err := DecryptSymmetric(workspaceKeyInBytes, value_ciphertext, value_tag, value_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
env := models.SingleEnvironmentVariable{
|
||||
Key: string(plainTextKey),
|
||||
Value: string(plainTextValue),
|
||||
Type: string(secret.Type),
|
||||
ID: secret.ID,
|
||||
}
|
||||
|
||||
listOfEnv = append(listOfEnv, env)
|
||||
}
|
||||
|
||||
return listOfEnv, nil
|
||||
}
|
||||
|
||||
func GetSecretsFromAPIUsingCurrentLoggedInUser(envName string, userCreds models.UserCredentials) ([]models.SingleEnvironmentVariable, error) {
|
||||
log.Debugln("GetSecretsFromAPIUsingCurrentLoggedInUser", "envName", envName, "userCreds", userCreds)
|
||||
// check if user has configured a workspace
|
||||
workspaces, err := GetAllWorkSpaceConfigsStartingFromCurrentPath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to read workspace file(s):", err)
|
||||
}
|
||||
|
||||
// create http client
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(userCreds.JTWToken).
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(serviceToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
secrets := []models.SingleEnvironmentVariable{}
|
||||
for _, workspace := range workspaces {
|
||||
secretsFromAPI, err := getSecretsByWorkspaceIdAndEnvName(*httpClient, envName, workspace, userCreds)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetSecretsFromAPIUsingCurrentLoggedInUser: Unable to get secrets by workspace id and env name")
|
||||
}
|
||||
|
||||
secrets = append(secrets, secretsFromAPI...)
|
||||
serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
|
||||
}
|
||||
|
||||
return secrets, nil
|
||||
encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{
|
||||
WorkspaceId: serviceTokenDetails.Workspace,
|
||||
EnvironmentName: serviceTokenDetails.Environment,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return 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)
|
||||
}
|
||||
|
||||
plainTextWorkspaceKey, err := crypto.DecryptSymmetric(decodedSymmetricEncryptionDetails.Key, decodedSymmetricEncryptionDetails.Cipher, decodedSymmetricEncryptionDetails.Tag, decodedSymmetricEncryptionDetails.IV)
|
||||
if err != nil {
|
||||
return 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 plainTextSecrets, nil
|
||||
}
|
||||
|
||||
func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, projectId string) ([]models.SingleEnvironmentVariable, error) {
|
||||
if infisicalToken == "" || projectId == "" || envName == "" {
|
||||
return nil, errors.New("infisical token, project id and or environment name cannot be empty")
|
||||
}
|
||||
splitToken := strings.Split(infisicalToken, ",")
|
||||
JTWToken := splitToken[0]
|
||||
temPrivateKey := splitToken[1]
|
||||
|
||||
// create http client
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(JTWToken).
|
||||
func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string) ([]models.SingleEnvironmentVariable, error) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
var pullSecretsByInfisicalTokenResponse models.PullSecretsByInfisicalTokenResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetQueryParam("environment", envName).
|
||||
SetQueryParam("channel", "cli").
|
||||
SetResult(&pullSecretsByInfisicalTokenResponse).
|
||||
Get(fmt.Sprintf("%v/v1/secret/%v/service-token", INFISICAL_URL, projectId))
|
||||
request := api.GetEncryptedWorkspaceKeyRequest{
|
||||
WorkspaceId: workspaceId,
|
||||
}
|
||||
|
||||
workspaceKeyResponse, err := api.CallGetEncryptedWorkspaceKey(httpClient, request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
|
||||
}
|
||||
|
||||
encryptedWorkspaceKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey)
|
||||
encryptedWorkspaceKeySenderPublicKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Sender.PublicKey)
|
||||
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce)
|
||||
currentUsersPrivateKey, _ := base64.StdEncoding.DecodeString(receiversPrivateKey)
|
||||
plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||
|
||||
encryptedSecrets, err := api.CallGetSecretsV2(httpClient, api.GetEncryptedSecretsV2Request{
|
||||
WorkspaceId: workspaceId,
|
||||
EnvironmentName: environmentName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return nil, fmt.Errorf(response.Status())
|
||||
}
|
||||
|
||||
// Get workspace key
|
||||
workspaceKey, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.EncryptedKey)
|
||||
plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
|
||||
}
|
||||
|
||||
nonce, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.Nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
senderPublicKey, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.Sender.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(temPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// workspaceKeyInBytes, _ := box.Open(nil, workspaceKey, (*[24]byte)(nonce), (*[32]byte)(senderPublicKey), (*[32]byte)(currentUsersPrivateKey))
|
||||
workspaceKeyInBytes := DecryptAsymmetric(workspaceKey, nonce, senderPublicKey, currentUsersPrivateKey)
|
||||
var listOfEnv []models.SingleEnvironmentVariable
|
||||
|
||||
for _, secret := range pullSecretsByInfisicalTokenResponse.Secrets {
|
||||
key_iv, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Iv)
|
||||
key_tag, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Tag)
|
||||
key_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Ciphertext)
|
||||
|
||||
plainTextKey, err := DecryptSymmetric(workspaceKeyInBytes, key_ciphertext, key_tag, key_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value_iv, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Iv)
|
||||
value_tag, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Tag)
|
||||
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Ciphertext)
|
||||
|
||||
plainTextValue, err := DecryptSymmetric(workspaceKeyInBytes, value_ciphertext, value_tag, value_iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
env := models.SingleEnvironmentVariable{
|
||||
Key: string(plainTextKey),
|
||||
Value: string(plainTextValue),
|
||||
Type: string(secret.Type),
|
||||
ID: secret.ID,
|
||||
}
|
||||
|
||||
listOfEnv = append(listOfEnv, env)
|
||||
}
|
||||
|
||||
return listOfEnv, nil
|
||||
return plainTextSecrets, nil
|
||||
}
|
||||
|
||||
func GetAllEnvironmentVariables(projectId string, envName string) ([]models.SingleEnvironmentVariable, error) {
|
||||
func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVariable, error) {
|
||||
infisicalToken := os.Getenv(INFISICAL_TOKEN_NAME)
|
||||
|
||||
if infisicalToken == "" {
|
||||
hasUserLoggedInbefore, loggedInUserEmail, err := IsUserLoggedIn()
|
||||
RequireLocalWorkspaceFile()
|
||||
RequireLogin()
|
||||
log.Debug("Trying to fetch secrets using logged in details")
|
||||
|
||||
loggedInUserDetails, err := GetCurrentLoggedInUserDetails()
|
||||
if err != nil {
|
||||
log.Info("Unexpected issue occurred while checking login status. To see more details, add flag --debug")
|
||||
log.Debugln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !hasUserLoggedInbefore {
|
||||
log.Infoln("No logged in user. To login, please run command [infisical login]")
|
||||
return nil, fmt.Errorf("user not logged in")
|
||||
}
|
||||
|
||||
userCreds, err := GetUserCredsFromKeyRing(loggedInUserEmail)
|
||||
workspaceFile, err := GetWorkSpaceFromFile()
|
||||
if err != nil {
|
||||
log.Infoln("Unable to get user creds from key ring")
|
||||
log.Debug(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Should be based on flag. I.e only get all workspaces if desired, otherwise only get the one in the current root of project
|
||||
workspaceConfigs, err := GetAllWorkSpaceConfigsStartingFromCurrentPath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to check if you have a %s file in your current directory", INFISICAL_WORKSPACE_CONFIG_FILE_NAME)
|
||||
}
|
||||
|
||||
if len(workspaceConfigs) == 0 {
|
||||
log.Infoln("Your local project is not connected to a Infisical project yet. Run command [infisical init]")
|
||||
return nil, fmt.Errorf("project not initialized")
|
||||
}
|
||||
|
||||
envsFromApi, err := GetSecretsFromAPIUsingCurrentLoggedInUser(envName, userCreds)
|
||||
if err != nil {
|
||||
log.Errorln("Something went wrong when pulling secrets using your logged in credentials. If the issue persists, double check your project id/try logging in again.")
|
||||
log.Debugln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return envsFromApi, nil
|
||||
secrets, err := GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, envName)
|
||||
return secrets, err
|
||||
|
||||
} else {
|
||||
envsFromApi, err := GetSecretsFromAPIUsingInfisicalToken(infisicalToken, envName, projectId)
|
||||
if err != nil {
|
||||
log.Errorln("Something went wrong when pulling secrets using your Infisical token. Double check the token, project id or environment name (dev, prod, ect.)")
|
||||
log.Debugln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return envsFromApi, nil
|
||||
log.Debug("Trying to fetch secrets using service token")
|
||||
return GetPlainTextSecretsViaServiceToken(infisicalToken)
|
||||
}
|
||||
}
|
||||
|
||||
func GetWorkSpacesFromAPI(userCreds models.UserCredentials) (workspaces []models.Workspace, err error) {
|
||||
// create http client
|
||||
httpClient := resty.New().
|
||||
SetAuthToken(userCreds.JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
var getWorkSpacesResponse models.GetWorkSpacesResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&getWorkSpacesResponse).
|
||||
Get(fmt.Sprintf("%v/v1/workspace", INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
return nil, fmt.Errorf("ops, unsuccessful response code. [response=%v]", response)
|
||||
}
|
||||
|
||||
return getWorkSpacesResponse.Workspaces, nil
|
||||
}
|
||||
|
||||
func getExpandedEnvVariable(secrets []models.SingleEnvironmentVariable, variableWeAreLookingFor string, hashMapOfCompleteVariables map[string]string, hashMapOfSelfRefs map[string]string) string {
|
||||
if value, found := hashMapOfCompleteVariables[variableWeAreLookingFor]; found {
|
||||
return value
|
||||
@ -351,6 +194,8 @@ func SubstituteSecrets(secrets []models.SingleEnvironmentVariable) []models.Sing
|
||||
return expandedSecrets
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// if two secrets with the same name are found, the one that has type `personal` will be in the returned list
|
||||
func OverrideWithPersonalSecrets(secrets []models.SingleEnvironmentVariable) []models.SingleEnvironmentVariable {
|
||||
personalSecret := make(map[string]models.SingleEnvironmentVariable)
|
||||
@ -359,46 +204,80 @@ func OverrideWithPersonalSecrets(secrets []models.SingleEnvironmentVariable) []m
|
||||
|
||||
for _, secret := range secrets {
|
||||
if secret.Type == PERSONAL_SECRET_TYPE_NAME {
|
||||
personalSecret[secret.Key] = models.SingleEnvironmentVariable{
|
||||
Key: secret.Key,
|
||||
Value: secret.Value,
|
||||
Type: secret.Type,
|
||||
}
|
||||
personalSecret[secret.Key] = secret
|
||||
}
|
||||
|
||||
if secret.Type == SHARED_SECRET_TYPE_NAME {
|
||||
sharedSecret[secret.Key] = models.SingleEnvironmentVariable{
|
||||
Key: secret.Key,
|
||||
Value: secret.Value,
|
||||
Type: secret.Type,
|
||||
}
|
||||
sharedSecret[secret.Key] = secret
|
||||
}
|
||||
}
|
||||
|
||||
for _, secret := range secrets {
|
||||
for _, secret := range sharedSecret {
|
||||
personalValue, personalExists := personalSecret[secret.Key]
|
||||
sharedValue, sharedExists := sharedSecret[secret.Key]
|
||||
|
||||
if personalExists && sharedExists || personalExists && !sharedExists {
|
||||
if personalExists {
|
||||
secretsToReturn = append(secretsToReturn, personalValue)
|
||||
} else {
|
||||
secretsToReturn = append(secretsToReturn, sharedValue)
|
||||
secretsToReturn = append(secretsToReturn, secret)
|
||||
}
|
||||
}
|
||||
|
||||
return secretsToReturn
|
||||
}
|
||||
|
||||
func IsSecretEnvironmentValid(env string) bool {
|
||||
if env == "prod" || env == "dev" || env == "test" || env == "staging" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func GetPlainTextSecrets(key []byte, encryptedSecrets api.GetEncryptedSecretsV2Response) ([]models.SingleEnvironmentVariable, error) {
|
||||
plainTextSecrets := []models.SingleEnvironmentVariable{}
|
||||
for _, secret := range encryptedSecrets {
|
||||
// Decrypt key
|
||||
key_iv, err := base64.StdEncoding.DecodeString(secret.SecretKeyIV)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode secret IV for secret key")
|
||||
}
|
||||
|
||||
func IsSecretTypeValid(s string) bool {
|
||||
if s == "personal" || s == "shared" {
|
||||
return true
|
||||
key_tag, err := base64.StdEncoding.DecodeString(secret.SecretKeyTag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode secret authentication tag for secret key")
|
||||
}
|
||||
|
||||
key_ciphertext, err := base64.StdEncoding.DecodeString(secret.SecretKeyCiphertext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode secret cipher text for secret key")
|
||||
}
|
||||
|
||||
plainTextKey, err := crypto.DecryptSymmetric(key, key_ciphertext, key_tag, key_iv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to symmetrically decrypt secret key")
|
||||
}
|
||||
|
||||
// Decrypt value
|
||||
value_iv, err := base64.StdEncoding.DecodeString(secret.SecretValueIV)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode secret IV for secret value")
|
||||
}
|
||||
|
||||
value_tag, err := base64.StdEncoding.DecodeString(secret.SecretValueTag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode secret authentication tag for secret value")
|
||||
}
|
||||
|
||||
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValueCiphertext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode secret cipher text for secret key")
|
||||
}
|
||||
|
||||
plainTextValue, err := crypto.DecryptSymmetric(key, value_ciphertext, value_tag, value_iv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to symmetrically decrypt secret value")
|
||||
}
|
||||
|
||||
plainTextSecret := models.SingleEnvironmentVariable{
|
||||
Key: string(plainTextKey),
|
||||
Value: string(plainTextValue),
|
||||
Type: string(secret.Type),
|
||||
ID: secret.ID,
|
||||
}
|
||||
|
||||
plainTextSecrets = append(plainTextSecrets, plainTextSecret)
|
||||
}
|
||||
return false
|
||||
|
||||
return plainTextSecrets, nil
|
||||
}
|
||||
|
@ -29,13 +29,13 @@ func GetKeyRing() (keyring.Keyring, error) {
|
||||
|
||||
keyringInstanceConfig := keyring.Config{
|
||||
FilePasswordFunc: fileKeyringPassphrasePrompt,
|
||||
ServiceName: SERVICE_NAME,
|
||||
LibSecretCollectionName: SERVICE_NAME,
|
||||
KWalletAppID: SERVICE_NAME,
|
||||
KWalletFolder: SERVICE_NAME,
|
||||
ServiceName: KEYRING_SERVICE_NAME,
|
||||
LibSecretCollectionName: KEYRING_SERVICE_NAME,
|
||||
KWalletAppID: KEYRING_SERVICE_NAME,
|
||||
KWalletFolder: KEYRING_SERVICE_NAME,
|
||||
KeychainTrustApplication: true,
|
||||
WinCredPrefix: SERVICE_NAME,
|
||||
FileDir: fmt.Sprintf("~/%s-file-vault", SERVICE_NAME),
|
||||
WinCredPrefix: KEYRING_SERVICE_NAME,
|
||||
FileDir: fmt.Sprintf("~/%s-file-vault", KEYRING_SERVICE_NAME),
|
||||
KeychainAccessibleWhenUnlocked: true,
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user