Added .NET to available frameworks

This commit is contained in:
Vladyslav Matsiiako
2022-12-28 12:02:42 -05:00
committed by Maidul Islam
parent b062c44742
commit 618dc10e45
11 changed files with 348 additions and 15 deletions

View File

@@ -270,13 +270,13 @@ We're currently setting the foundation and building [integrations](https://infis
</tr>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/rails?ref=github.com">
✔️ Ruby on Rails
<a href="https://infisical.com/docs/integrations/frameworks/vue?ref=github.com">
✔️ Vue
</a>
</td>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/vue?ref=github.com">
✔️ Vue
<a href="https://infisical.com/docs/integrations/frameworks/rails?ref=github.com">
✔️ Ruby on Rails
</a>
</td>
</tr>
@@ -292,6 +292,16 @@ We're currently setting the foundation and building [integrations](https://infis
</a>
</td>
</tr>
<tr>
<td align="left" valign="middle">
<a href="https://infisical.com/docs/integrations/frameworks/dotnet?ref=github.com">
✔️ .NET
</a>
</td>
<td align="left" valign="middle">
And more...
</td>
</tr>
</tbody>
</table>

View File

@@ -33,7 +33,8 @@ const secretSchema = new Schema<ISecret>(
{
version: {
type: Number,
required: true
required: true,
default: 1
},
workspace: {
type: Schema.Types.ObjectId,

View File

@@ -1,4 +1,137 @@
import express from 'express';
import express, { Request, Response } from 'express';
import { requireAuth, validateRequest } from '../../middleware';
import { ISecret, Secret } from '../../models';
import { decryptSymmetric } from '../../utils/crypto';
import { getLogger } from '../../utils/logger';
import { body, param, query, check } from 'express-validator';
import { BadRequestError } from '../../utils/errors';
const router = express.Router();
/**
* Create a single secret for a given workspace and environment
*/
router.post(
'/', requireAuth,
body('secret').exists().isObject(),
async (req: Request, res: Response) => {
try {
const { secret }: { secret: ISecret[] } = req.body;
const newlyCreatedSecret = await Secret.create(secret)
res.status(200).json(newlyCreatedSecret)
} catch {
throw BadRequestError({ message: "Unable to create the secret" })
}
}
);
/**
* Create many secrets
*/
router.post(
'/bulk-create', requireAuth,
body('secrets').exists().isArray().custom((value) => value.every((item: ISecret) => typeof item === 'object')),
async (req: Request, res: Response) => {
try {
const { secrets }: { secrets: ISecret[] } = req.body;
const newlyCreatedSecrets = await Secret.insertMany(secrets)
res.status(200).json(newlyCreatedSecrets)
} catch {
throw BadRequestError({ message: "Unable to create the secret" })
}
}
);
/**
* Get a single secret by secret id
*/
router.get(
'/:secretId', requireAuth, param('secretId').exists().trim(),
validateRequest, async (req: Request, res: Response) => {
try {
const secretFromDB = await Secret.findById(req.params.secretId)
return res.status(200).send(secretFromDB);
} catch (e) {
throw BadRequestError({ message: "Unable to find the requested secret" })
}
}
);
/**
* Get a single secret by secret id
*/
router.get(
'/:bulk', requireAuth, param('secretId').exists().trim(),
validateRequest, async (req: Request, res: Response) => {
try {
const secretFromDB = await Secret.findById(req.params.secretId)
return res.status(200).send(secretFromDB);
} catch (e) {
throw BadRequestError({ message: "Unable to find the requested secret" })
}
}
);
/**
* Delete a single secret by secret id
*/
router.delete(
'/:secretId',
requireAuth,
param('secretId').exists().trim(),
validateRequest, async (req: Request, res: Response) => {
try {
const secretFromDB = await Secret.deleteOne({
_id: req.params.secretId
})
return res.status(200).send(secretFromDB);
} catch (e) {
throw BadRequestError({ message: "Unable to find the requested secret" })
}
}
);
/**
* Delete many secrets by secret ids
*/
router.delete(
'/batch',
requireAuth,
body('secretIds').exists().isArray(),
validateRequest, async (req: Request, res: Response) => {
try {
const secretIdsToDelete: string[] = req.body.secretIds
const secretFromDB = await Secret.deleteMany({
_id: { $in: secretIdsToDelete }
})
return res.status(200).send(secretFromDB);
} catch (error) {
throw BadRequestError({ message: `Unable to delete the requested secrets by ids [${req.body.secretIds}]` })
}
}
);
/**
* Apply modifications to many existing secrets
*/
router.patch(
'/bulk-update',
requireAuth,
body('secrets').exists().isArray().custom((value) => value.every((item: ISecret) => typeof item === 'object')),
validateRequest, async (req: Request, res: Response) => {
try {
const { secrets }: { secrets: ISecret[] } = req.body;
const operations = secrets.map((secretToUpdate: ISecret) => ({
updateOne: { filter: { _id: secretToUpdate._id }, update: secretToUpdate },
}));
const bulkModificationInfo = await Secret.bulkWrite(operations);
return res.status(200).json(bulkModificationInfo)
} catch (error) {
throw BadRequestError({ message: `Unable to process the bulk update. Double check the ids of the secrets` })
}
}
);
export default router;

View File

@@ -13,6 +13,7 @@ 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
@@ -34,6 +35,7 @@ require (
)
require (
github.com/Luzifer/go-openssl v2.0.0+incompatible
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

View File

@@ -2,6 +2,10 @@ 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=
@@ -96,15 +100,20 @@ 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-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=
@@ -116,6 +125,7 @@ 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=

103
cli/packages/cmd/secrets.go Normal file
View File

@@ -0,0 +1,103 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"fmt"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/Infisical/infisical-merge/packages/visualize"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var secretsCmd = &cobra.Command{
Example: `infisical secrets"`,
Short: "Used to create, read update and delete secrets",
Use: "secrets",
DisableFlagsInUseLine: true,
PreRun: toggleDebug,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
secrets, err := util.GetAllEnvironmentVariables("", "dev")
secrets = util.SubstituteSecrets(secrets)
if err != nil {
log.Debugln(err)
return
}
visualize.PrintAllSecretDetails(secrets)
},
}
var secretsGetCmd = &cobra.Command{
Example: `secrets get <secret name A> <secret name B>..."`,
Short: "Used to retrieve secrets by name",
Use: "get [secrets]",
DisableFlagsInUseLine: true,
Args: cobra.MinimumNArgs(1),
PreRun: toggleDebug,
Run: getSecretsByNames,
}
var secretsSetCmd = &cobra.Command{
Example: `secrets set <secret name A> <secret value A> <secret name B> <secret value B>..."`,
Short: "Used update retrieve secrets by name",
Use: "set [secrets]",
DisableFlagsInUseLine: true,
PreRun: toggleDebug,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("set secret")
},
}
var secretsDeleteCmd = &cobra.Command{
Example: `secrets delete <secret name A> <secret name B>..."`,
Short: "Used to delete secrets by name",
Use: "delete [secrets]",
DisableFlagsInUseLine: true,
PreRun: toggleDebug,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Delete secret")
},
}
func init() {
secretsCmd.AddCommand(secretsGetCmd)
secretsCmd.AddCommand(secretsSetCmd)
secretsCmd.AddCommand(secretsDeleteCmd)
rootCmd.AddCommand(secretsCmd)
}
func getSecretsByNames(cmd *cobra.Command, args []string) {
secrets, err := util.GetAllEnvironmentVariables("", "dev")
if err != nil {
log.Error("Unable to retrieve secrets. Run with -d to see full logs")
log.Debug(err)
}
requestedSecrets := []models.SingleEnvironmentVariable{}
secretsMap := make(map[string]models.SingleEnvironmentVariable)
for _, secret := range secrets {
secretsMap[secret.Key] = secret
}
for _, secretKeyFromArg := range args {
if value, ok := secretsMap[secretKeyFromArg]; ok {
requestedSecrets = append(requestedSecrets, value)
} else {
requestedSecrets = append(requestedSecrets, models.SingleEnvironmentVariable{
Key: secretKeyFromArg,
Type: "NOT FOUND",
Value: "NOT FOUND",
})
}
}
visualize.PrintAllSecretDetails(requestedSecrets)
}

View File

@@ -3,21 +3,26 @@ package util
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
"golang.org/x/crypto/nacl/box"
)
func DecryptSymmetric(key []byte, encryptedPrivateKey []byte, tag []byte, IV []byte) ([]byte, error) {
// will decrypt cipher text to plain text using iv and tag
func DecryptSymmetric(key []byte, cipherText []byte, tag []byte, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCMWithNonceSize(block, len(IV))
aesgcm, err := cipher.NewGCMWithNonceSize(block, len(iv))
if err != nil {
return nil, err
}
var nonce = IV
var ciphertext = append(encryptedPrivateKey, tag...)
var nonce = iv
var ciphertext = append(cipherText, tag...) // the aesgcm open method expects auth tag at the end of the cipher text
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
@@ -26,3 +31,46 @@ func DecryptSymmetric(key []byte, encryptedPrivateKey []byte, tag []byte, IV []b
return plaintext, nil
}
func GenerateNewKey() (newKey []byte, keyErr error) {
key := make([]byte, 16) // block size defaults to 16 so this is fine
_, err := rand.Read(key)
return key, err
}
// Will encrypt a plain text with the provided key
func EncryptSymmetric(plaintext []byte, key []byte) (cipherText []byte, iv []byte, tag []byte, err error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, nil, err
}
aesgcm, err := cipher.NewGCMWithNonceSize(block, 16) // default is 12, 16 because https://github.com/Infisical/infisical/blob/bea0ff6e05a4de73a5db625d4ae181a015b50855/backend/src/utils/aes-gcm.ts#L4
if err != nil {
return nil, nil, nil, err
}
// create a nonce
nonce := make([]byte, aesgcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err)
}
ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
ciphertextOnly := ciphertext[:len(ciphertext)-16] // combines the auth tag with the cipher text so we need to extract it
authTag := ciphertext[len(ciphertext)-16:]
return ciphertextOnly, nonce, authTag, nil
}
func DecryptAsymmetric(ciphertext []byte, nonce []byte, publicKey []byte, privateKey []byte) (plainText []byte) {
plainTextToReturn, _ := box.Open(nil, ciphertext, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey))
return plainTextToReturn
}
func EncryptAssymmetric(message []byte, nonce []byte, publicKey []byte, privateKey []byte) (encryptedMessage []byte) {
encryptedPlainText := box.Seal(nil, message, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey))
return encryptedPlainText
}

View File

@@ -11,7 +11,6 @@ import (
"github.com/Infisical/infisical-merge/packages/models"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/nacl/box"
)
const PERSONAL_SECRET_TYPE_NAME = "personal"
@@ -56,7 +55,7 @@ func getSecretsByWorkspaceIdAndEnvName(httpClient resty.Client, envName string,
}
// log.Debugln("workspaceKey", workspaceKey, "nonce", nonce, "senderPublicKey", senderPublicKey, "currentUsersPrivateKey", currentUsersPrivateKey)
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 pullSecretsRequestResponse.Secrets {
@@ -166,7 +165,8 @@ func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string,
return nil, err
}
workspaceKeyInBytes, _ := box.Open(nil, workspaceKey, (*[24]byte)(nonce), (*[32]byte)(senderPublicKey), (*[32]byte)(currentUsersPrivateKey))
// 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 {
@@ -223,6 +223,7 @@ func GetAllEnvironmentVariables(projectId string, envName string) ([]models.Sing
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)

View File

@@ -0,0 +1,14 @@
package visualize
import "github.com/Infisical/infisical-merge/packages/models"
func PrintAllSecretDetails(secrets []models.SingleEnvironmentVariable) {
rows := [][]string{}
for _, secret := range secrets {
rows = append(rows, []string{secret.Key, secret.Value, secret.Type})
}
headers := []string{"Secret name", "Secret vaule", "Secret type"}
Table(headers, rows)
}

View File

@@ -6,13 +6,23 @@ import (
"github.com/jedib0t/go-pretty/table"
)
type TableOptions struct {
Title string
}
// func GetDefaultTableOptions() TableOptions{
// return TableOptions{
// Title: "",
// }
// }
// Given 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.SetTitle(tableOptions.Title)
t.Style().Options.DrawBorder = true
t.Style().Options.SeparateHeader = true
t.Style().Options.SeparateColumns = true

View File

@@ -166,7 +166,8 @@
"integrations/frameworks/django",
"integrations/frameworks/flask",
"integrations/frameworks/laravel",
"integrations/frameworks/rails"
"integrations/frameworks/rails",
"integrations/frameworks/dotnet"
]
},
{