mirror of
				https://github.com/mitchell/selfpass.git
				synced 2025-11-03 23:35:25 +00:00 
			
		
		
		
	Implemented all but update from cli client to server;
solidified encryption; setup deployment mechanism for GCP
This commit is contained in:
		
							parent
							
								
									cd24f6e848
								
							
						
					
					
						commit
						c5ae0b4ddc
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
.idea
 | 
					.idea
 | 
				
			||||||
bin
 | 
					bin
 | 
				
			||||||
certs
 | 
					certs
 | 
				
			||||||
db
 | 
					data
 | 
				
			||||||
 | 
					*.tar
 | 
				
			||||||
 | 
					*.enc
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,10 @@ FROM golang:1.11.5 as build
 | 
				
			||||||
WORKDIR /go/src/github.com/mitchell/selfpass
 | 
					WORKDIR /go/src/github.com/mitchell/selfpass
 | 
				
			||||||
COPY . .
 | 
					COPY . .
 | 
				
			||||||
ENV GO111MODULE=on
 | 
					ENV GO111MODULE=on
 | 
				
			||||||
 | 
					RUN make gen-certs-go
 | 
				
			||||||
RUN make build
 | 
					RUN make build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FROM debian:stable-20190326-slim
 | 
					FROM debian:stable-20190506-slim
 | 
				
			||||||
WORKDIR /usr/bin
 | 
					WORKDIR /usr/bin
 | 
				
			||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/bin/server .
 | 
					COPY --from=build /go/src/github.com/mitchell/selfpass/bin/server .
 | 
				
			||||||
ENTRYPOINT ["server"]
 | 
					ENTRYPOINT ["server"]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										33
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								Makefile
									
									
									
									
									
								
							| 
						 | 
					@ -1,7 +1,8 @@
 | 
				
			||||||
.PHONY: all build clean format test docker-build
 | 
					.PHONY: all build clean format test docker-build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
build: clean format test
 | 
					build: clean format
 | 
				
			||||||
	go build --o ./bin/server ./cmd/server/server.go
 | 
						env CGO_ENABLED=0 go build -o ./bin/server ./cmd/server
 | 
				
			||||||
 | 
						rm ./cmd/server/certs.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clean:
 | 
					clean:
 | 
				
			||||||
	rm -rf ./bin
 | 
						rm -rf ./bin
 | 
				
			||||||
| 
						 | 
					@ -10,9 +11,32 @@ clean:
 | 
				
			||||||
docker:
 | 
					docker:
 | 
				
			||||||
	docker-compose build
 | 
						docker-compose build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
start:
 | 
					up:
 | 
				
			||||||
	docker-compose up
 | 
						docker-compose up
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					upd:
 | 
				
			||||||
 | 
						docker-compose up -d
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					down:
 | 
				
			||||||
 | 
						docker-compose down
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					machine-create-google:
 | 
				
			||||||
 | 
						docker-machine create --driver google \
 | 
				
			||||||
 | 
						    --google-address m-selfpass \
 | 
				
			||||||
 | 
						    --google-project selfpass-241808 \
 | 
				
			||||||
 | 
						    --google-machine-type n1-standard-1 \
 | 
				
			||||||
 | 
						    --google-machine-image https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-9-stretch-v20190514 \
 | 
				
			||||||
 | 
						    selfpass01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					machine-rm:
 | 
				
			||||||
 | 
						docker-machine rm selfpass01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					machine-ssh:
 | 
				
			||||||
 | 
						docker-machine ssh selfpass01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					machine-env:
 | 
				
			||||||
 | 
						docker-machine env selfpass01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
format:
 | 
					format:
 | 
				
			||||||
	gofmt -w -s -l .
 | 
						gofmt -w -s -l .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,5 +60,8 @@ gen-server-cert:
 | 
				
			||||||
gen-client-cert:
 | 
					gen-client-cert:
 | 
				
			||||||
	cd certs && cfssl gencert -ca ca.pem -ca-key ca-key.pem csr.json | cfssljson -bare client
 | 
						cd certs && cfssl gencert -ca ca.pem -ca-key ca-key.pem csr.json | cfssljson -bare client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gen-certs-go:
 | 
				
			||||||
 | 
						./gen_certs_go.sh > ./cmd/server/certs.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test:
 | 
					test:
 | 
				
			||||||
	go test -cover ./...
 | 
						go test -cover ./...
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										52
									
								
								cli/commands/decrypt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								cli/commands/decrypt.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"github.com/spf13/viper"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mitchell/selfpass/credentials/commands"
 | 
				
			||||||
 | 
						"github.com/mitchell/selfpass/crypto"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func makeDecrypt(masterpass string, cfg *viper.Viper) *cobra.Command {
 | 
				
			||||||
 | 
						decryptCmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "decrypt [file]",
 | 
				
			||||||
 | 
							Short: "Decrypt a file using your masterpass and secret key",
 | 
				
			||||||
 | 
							Long: `Decrypt a file using your masterpass and secret key, and replace the old file with
 | 
				
			||||||
 | 
					the new file.`,
 | 
				
			||||||
 | 
							Args: cobra.ExactArgs(1),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Run: func(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
								file := args[0]
 | 
				
			||||||
 | 
								fileout := file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if file[len(file)-4:] == ".enc" {
 | 
				
			||||||
 | 
									fileout = file[:len(file)-4]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								contents, err := ioutil.ReadFile(file)
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key, err := hex.DecodeString(cfg.GetString(commands.KeyPrivateKey))
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								contents, err = crypto.CBCDecrypt(passkey, contents)
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								check(ioutil.WriteFile(fileout, contents, 0600))
 | 
				
			||||||
 | 
								check(os.Remove(file))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Println("Decrypted file: ", fileout)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return decryptCmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								cli/commands/encrypt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								cli/commands/encrypt.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"github.com/spf13/viper"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mitchell/selfpass/credentials/commands"
 | 
				
			||||||
 | 
						"github.com/mitchell/selfpass/crypto"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func makeEncrypt(masterpass string, cfg *viper.Viper) *cobra.Command {
 | 
				
			||||||
 | 
						encryptCmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "encrypt [file]",
 | 
				
			||||||
 | 
							Short: "Encrypt a file using your masterpass and secret key",
 | 
				
			||||||
 | 
							Long: `Encrypt a file using your masterpass and secret key, and replace the old file with the
 | 
				
			||||||
 | 
					new file.`,
 | 
				
			||||||
 | 
							Args: cobra.ExactArgs(1),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Run: func(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
								file := args[0]
 | 
				
			||||||
 | 
								fileEnc := file + ".enc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								contents, err := ioutil.ReadFile(file)
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key, err := hex.DecodeString(cfg.GetString(commands.KeyPrivateKey))
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								contents, err = crypto.CBCEncrypt(passkey, contents)
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								check(ioutil.WriteFile(fileEnc, contents, 0600))
 | 
				
			||||||
 | 
								check(os.Remove(file))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Println("Encrypted file: ", fileEnc)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return encryptCmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package cmd
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
| 
						 | 
					@ -11,10 +11,10 @@ import (
 | 
				
			||||||
	"github.com/spf13/viper"
 | 
						"github.com/spf13/viper"
 | 
				
			||||||
	"gopkg.in/AlecAivazis/survey.v1"
 | 
						"gopkg.in/AlecAivazis/survey.v1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/cmds"
 | 
						"github.com/mitchell/selfpass/credentials/commands"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeInitCmd(cfg *viper.Viper) *cobra.Command {
 | 
					func makeInit(cfg *viper.Viper) *cobra.Command {
 | 
				
			||||||
	initCmd := &cobra.Command{
 | 
						initCmd := &cobra.Command{
 | 
				
			||||||
		Use:   "init",
 | 
							Use:   "init",
 | 
				
			||||||
		Short: "This command initializes SPC for the first time",
 | 
							Short: "This command initializes SPC for the first time",
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@ the users private key, and server certificates. (All of which will be encrypted)
 | 
				
			||||||
				privateKey  = strings.Replace(uuid.New().String(), "-", "", -1)
 | 
									privateKey  = strings.Replace(uuid.New().String(), "-", "", -1)
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			prompt = &survey.Password{Message: "Master password:"}
 | 
								prompt = &survey.Password{Message: "New master password:"}
 | 
				
			||||||
			check(survey.AskOne(prompt, &masterpass, nil))
 | 
								check(survey.AskOne(prompt, &masterpass, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			prompt = &survey.Password{Message: "Confirm master password:"}
 | 
								prompt = &survey.Password{Message: "Confirm master password:"}
 | 
				
			||||||
| 
						 | 
					@ -69,14 +69,14 @@ the users private key, and server certificates. (All of which will be encrypted)
 | 
				
			||||||
			key, err := ioutil.ReadFile(keyFile)
 | 
								key, err := ioutil.ReadFile(keyFile)
 | 
				
			||||||
			check(err)
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			cfg.Set(cmds.KeyConnConfig, map[string]string{
 | 
								cfg.Set(keyConnConfig, map[string]string{
 | 
				
			||||||
				"target": target,
 | 
									"target": target,
 | 
				
			||||||
				"ca":     string(ca),
 | 
									"ca":     string(ca),
 | 
				
			||||||
				"cert":   string(cert),
 | 
									"cert":   string(cert),
 | 
				
			||||||
				"key":    string(key),
 | 
									"key":    string(key),
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			cfg.Set(cmds.KeyPrivateKey, privateKey)
 | 
								cfg.Set(commands.KeyPrivateKey, privateKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err := cfg.WriteConfig(); err != nil {
 | 
								if err := cfg.WriteConfig(); err != nil {
 | 
				
			||||||
				home, err := homedir.Dir()
 | 
									home, err := homedir.Dir()
 | 
				
			||||||
| 
						 | 
					@ -1,56 +1,67 @@
 | 
				
			||||||
package cmd
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mitchellh/go-homedir"
 | 
						"github.com/mitchellh/go-homedir"
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
	"github.com/spf13/viper"
 | 
						"github.com/spf13/viper"
 | 
				
			||||||
	"gopkg.in/AlecAivazis/survey.v1"
 | 
						"gopkg.in/AlecAivazis/survey.v1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/cmds"
 | 
						"github.com/mitchell/selfpass/credentials/commands"
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/types"
 | 
						credtypes "github.com/mitchell/selfpass/credentials/types"
 | 
				
			||||||
	"github.com/mitchell/selfpass/crypto"
 | 
						"github.com/mitchell/selfpass/crypto"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Execute(ctx context.Context, initClient types.CredentialClientInit) {
 | 
					func Execute(initClient credtypes.CredentialClientInit) {
 | 
				
			||||||
	rootCmd := &cobra.Command{
 | 
						rootCmd := &cobra.Command{
 | 
				
			||||||
		Use:   "spc",
 | 
							Use:   "spc",
 | 
				
			||||||
		Short: "This is the CLI client for Selfpass.",
 | 
							Short: "This is the CLI client for Selfpass.",
 | 
				
			||||||
		Long: `This is the CLI client for Selfpass, the self-hosted password manager. With this tool you
 | 
							Long: `This is the CLI client for Selfpass, the self-hosted password manager. With this tool you
 | 
				
			||||||
can interact with the entire Selfpass API.`,
 | 
					can interact with the entire Selfpass API.`,
 | 
				
			||||||
 | 
							Version: "v0.1.0",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						rootCmd.InitDefaultHelpFlag()
 | 
				
			||||||
 | 
						rootCmd.InitDefaultVersionFlag()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfgFile := rootCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.spc.toml)")
 | 
						cfgFile := rootCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.spc.toml)")
 | 
				
			||||||
	rootCmd.PersistentFlags().Parse(os.Args)
 | 
						decryptCfg := rootCmd.Flags().Bool("decrypt-cfg", false, "decrypt config file")
 | 
				
			||||||
 | 
						check(rootCmd.ParseFlags(os.Args))
 | 
				
			||||||
	decryptCfg := rootCmd.Flags().Bool("decrypt-cfg", false, "unencrypt config file")
 | 
					 | 
				
			||||||
	rootCmd.Flags().Parse(os.Args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	encryptCfg := !*decryptCfg
 | 
						encryptCfg := !*decryptCfg
 | 
				
			||||||
	masterpass, cfg := openConfig(*cfgFile)
 | 
					
 | 
				
			||||||
	if encryptCfg && masterpass != "" {
 | 
						var masterpass string
 | 
				
			||||||
		defer encryptConfig(masterpass, cfg)
 | 
						var cfg *viper.Viper
 | 
				
			||||||
	}
 | 
						needsCfg := (len(os.Args) > 1 && !strings.Contains(strings.Join(os.Args, "--"), "--help")) || *decryptCfg
 | 
				
			||||||
	if *decryptCfg {
 | 
					
 | 
				
			||||||
		fmt.Println("Decrypting config file. It will auto-encrypt when you next run of spc.")
 | 
						if needsCfg {
 | 
				
			||||||
		return
 | 
							masterpass, cfg = openConfig(*cfgFile)
 | 
				
			||||||
 | 
							if encryptCfg && masterpass != "" {
 | 
				
			||||||
 | 
								defer encryptConfig(masterpass, cfg)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if *decryptCfg {
 | 
				
			||||||
 | 
								fmt.Println("Decrypting config file. It will auto-encrypt when you next run of spc.")
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rootCmd.AddCommand(makeInitCmd(cfg))
 | 
						rootCmd.AddCommand(makeInit(cfg))
 | 
				
			||||||
	rootCmd.AddCommand(cmds.MakeListCmd(makeInitClient(cfg, initClient)))
 | 
						rootCmd.AddCommand(makeEncrypt(masterpass, cfg))
 | 
				
			||||||
	rootCmd.AddCommand(cmds.MakeCreateCmd(masterpass, cfg, makeInitClient(cfg, initClient)))
 | 
						rootCmd.AddCommand(makeDecrypt(masterpass, cfg))
 | 
				
			||||||
	rootCmd.AddCommand(cmds.MakeGetCmd(masterpass, cfg, makeInitClient(cfg, initClient)))
 | 
						rootCmd.AddCommand(commands.MakeList(makeInitClient(cfg, initClient)))
 | 
				
			||||||
 | 
						rootCmd.AddCommand(commands.MakeCreate(masterpass, cfg, makeInitClient(cfg, initClient)))
 | 
				
			||||||
 | 
						rootCmd.AddCommand(commands.MakeGet(masterpass, cfg, makeInitClient(cfg, initClient)))
 | 
				
			||||||
 | 
						rootCmd.AddCommand(commands.MakeDelete(makeInitClient(cfg, initClient)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	check(rootCmd.Execute())
 | 
						check(rootCmd.Execute())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeInitClient(cfg *viper.Viper, initClient types.CredentialClientInit) cmds.CredentialClientInit {
 | 
					func makeInitClient(cfg *viper.Viper, initClient credtypes.CredentialClientInit) commands.CredentialClientInit {
 | 
				
			||||||
	return func(ctx context.Context) types.CredentialClient {
 | 
						return func(ctx context.Context) credtypes.CredentialClient {
 | 
				
			||||||
		connConfig := cfg.GetStringMapString(cmds.KeyConnConfig)
 | 
							connConfig := cfg.GetStringMapString(keyConnConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		client, err := initClient(
 | 
							client, err := initClient(
 | 
				
			||||||
			ctx,
 | 
								ctx,
 | 
				
			||||||
| 
						 | 
					@ -145,3 +156,5 @@ func check(err error) {
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const keyConnConfig = "connection"
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import (
 | 
				
			||||||
	"crypto/x509"
 | 
						"crypto/x509"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	stdlog "log"
 | 
						stdlog "log"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
| 
						 | 
					@ -28,13 +27,10 @@ var logger log.Logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		stop    = make(chan os.Signal, 1)
 | 
							stop     = make(chan os.Signal, 1)
 | 
				
			||||||
		dev     = flag.Bool("dev", false, "enables dev mode logging")
 | 
							jsonLogs = flag.Bool("json-logs", false, "enables json logging")
 | 
				
			||||||
		port    = flag.String("port", "8080", "specify the port to listen on")
 | 
							port     = flag.String("port", "8080", "specify the port to listen on")
 | 
				
			||||||
		crtFile = flag.String("cert", "/run/secrets/server_cert", "specify the cert file")
 | 
							verbose  = flag.Bool("v", false, "be more verbose")
 | 
				
			||||||
		keyFile = flag.String("key", "/run/secrets/server_key", "specify the private key file")
 | 
					 | 
				
			||||||
		caFile  = flag.String("ca", "/run/secrets/ca_cert", "specify the ca cert file")
 | 
					 | 
				
			||||||
		verbose = flag.Bool("v", false, "be more verbose")
 | 
					 | 
				
			||||||
		// tableName = flag.String(
 | 
							// tableName = flag.String(
 | 
				
			||||||
		// 	"credential-table-name",
 | 
							// 	"credential-table-name",
 | 
				
			||||||
		// 	"selfpass-credential",
 | 
							// 	"selfpass-credential",
 | 
				
			||||||
| 
						 | 
					@ -47,16 +43,13 @@ func main() {
 | 
				
			||||||
	signal.Notify(stop, syscall.SIGKILL)
 | 
						signal.Notify(stop, syscall.SIGKILL)
 | 
				
			||||||
	signal.Notify(stop, syscall.SIGTERM)
 | 
						signal.Notify(stop, syscall.SIGTERM)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logger = newLogger(os.Stdout, *dev)
 | 
						logger = newLogger(os.Stdout, *jsonLogs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	keypair, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
 | 
						keypair, err := tls.X509KeyPair([]byte(cert), []byte(key))
 | 
				
			||||||
	check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ca, err := ioutil.ReadFile(*caFile)
 | 
					 | 
				
			||||||
	check(err)
 | 
						check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	caPool := x509.NewCertPool()
 | 
						caPool := x509.NewCertPool()
 | 
				
			||||||
	caPool.AppendCertsFromPEM(ca)
 | 
						caPool.AppendCertsFromPEM([]byte(ca))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	creds := credentials.NewTLS(&tls.Config{
 | 
						creds := credentials.NewTLS(&tls.Config{
 | 
				
			||||||
		Certificates: []tls.Certificate{keypair},
 | 
							Certificates: []tls.Certificate{keypair},
 | 
				
			||||||
| 
						 | 
					@ -85,7 +78,6 @@ func main() {
 | 
				
			||||||
	_ = logger.Log(
 | 
						_ = logger.Log(
 | 
				
			||||||
		"message", "serving",
 | 
							"message", "serving",
 | 
				
			||||||
		"address", addr,
 | 
							"address", addr,
 | 
				
			||||||
		"dev", dev,
 | 
					 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go func() { check(srv.Serve(lis)) }()
 | 
						go func() { check(srv.Serve(lis)) }()
 | 
				
			||||||
| 
						 | 
					@ -95,13 +87,11 @@ func main() {
 | 
				
			||||||
	srv.GracefulStop()
 | 
						srv.GracefulStop()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newLogger(writer io.Writer, dev bool) log.Logger {
 | 
					func newLogger(writer io.Writer, jsonLogs bool) log.Logger {
 | 
				
			||||||
	var l log.Logger
 | 
					 | 
				
			||||||
	writer = log.NewSyncWriter(writer)
 | 
						writer = log.NewSyncWriter(writer)
 | 
				
			||||||
 | 
						l := log.NewLogfmtLogger(writer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if dev {
 | 
						if jsonLogs {
 | 
				
			||||||
		l = log.NewLogfmtLogger(writer)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		l = log.NewJSONLogger(writer)
 | 
							l = log.NewJSONLogger(writer)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	l = log.WithPrefix(l, "caller", log.Caller(5), "timestamp", log.DefaultTimestamp)
 | 
						l = log.WithPrefix(l, "caller", log.Caller(5), "timestamp", log.DefaultTimestamp)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,10 @@
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"github.com/mitchell/selfpass/cli/commands"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/mitchell/selfpass/cmd/spc/cmd"
 | 
					 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/repositories"
 | 
						"github.com/mitchell/selfpass/credentials/repositories"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	ctx := context.Background()
 | 
						commands.Execute(repositories.NewCredentialServiceClient)
 | 
				
			||||||
	cmd.Execute(ctx, repositories.NewCredentialServiceClient)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,63 +0,0 @@
 | 
				
			||||||
package cmds
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"encoding/base64"
 | 
					 | 
				
			||||||
	"encoding/hex"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/atotto/clipboard"
 | 
					 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
					 | 
				
			||||||
	"github.com/spf13/viper"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/mitchell/selfpass/crypto"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func MakeGetCmd(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
 | 
					 | 
				
			||||||
	getCmd := &cobra.Command{
 | 
					 | 
				
			||||||
		Use:   "get [id]",
 | 
					 | 
				
			||||||
		Short: "Get a credential info and copy password to clipboard",
 | 
					 | 
				
			||||||
		Long: `Get a credential's info and copy password to clipboard, from Selfpass server, after
 | 
					 | 
				
			||||||
decrypting password.`,
 | 
					 | 
				
			||||||
		Args: cobra.ExactArgs(1),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Run: func(cmd *cobra.Command, args []string) {
 | 
					 | 
				
			||||||
			ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
 | 
					 | 
				
			||||||
			defer cancel()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			cbcontents, err := clipboard.ReadAll()
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			restore := func(cbcontents string) {
 | 
					 | 
				
			||||||
				time.Sleep(time.Second * 5)
 | 
					 | 
				
			||||||
				clipboard.WriteAll(cbcontents)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			cred, err := initClient(ctx).Get(ctx, args[0])
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), key)
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			passbytes, err := base64.StdEncoding.DecodeString(cred.Password)
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			plainpass, err := crypto.CBCDecrypt(passkey, passbytes)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			check(clipboard.WriteAll(string(plainpass)))
 | 
					 | 
				
			||||||
			go restore(cbcontents)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			cjson, err := json.MarshalIndent(cred, "", "  ")
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
			fmt.Println(string(cjson))
 | 
					 | 
				
			||||||
			fmt.Println("Wrote password to clipboard.")
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return getCmd
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package cmds
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
| 
						 | 
					@ -17,5 +17,4 @@ func check(err error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const KeyConnConfig = "connection"
 | 
					 | 
				
			||||||
const KeyPrivateKey = "private_key"
 | 
					const KeyPrivateKey = "private_key"
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,15 @@
 | 
				
			||||||
package cmds
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"encoding/hex"
 | 
						"encoding/hex"
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"math/rand"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/atotto/clipboard"
 | 
						"github.com/atotto/clipboard"
 | 
				
			||||||
 | 
						"github.com/pquerna/otp/totp"
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
	"github.com/spf13/viper"
 | 
						"github.com/spf13/viper"
 | 
				
			||||||
	"gopkg.in/AlecAivazis/survey.v1"
 | 
						"gopkg.in/AlecAivazis/survey.v1"
 | 
				
			||||||
| 
						 | 
					@ -19,7 +18,11 @@ import (
 | 
				
			||||||
	"github.com/mitchell/selfpass/crypto"
 | 
						"github.com/mitchell/selfpass/crypto"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func MakeCreateCmd(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
 | 
					func MakeCreate(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
 | 
				
			||||||
 | 
						var length uint
 | 
				
			||||||
 | 
						var numbers bool
 | 
				
			||||||
 | 
						var specials bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	createCmd := &cobra.Command{
 | 
						createCmd := &cobra.Command{
 | 
				
			||||||
		Use:   "create",
 | 
							Use:   "create",
 | 
				
			||||||
		Short: "Create a credential in Selfpass",
 | 
							Short: "Create a credential in Selfpass",
 | 
				
			||||||
| 
						 | 
					@ -56,16 +59,21 @@ password.`,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			var ci types.CredentialInput
 | 
								var ci types.CredentialInput
 | 
				
			||||||
 | 
					 | 
				
			||||||
			check(survey.Ask(mdqs, &ci.MetadataInput))
 | 
								check(survey.Ask(mdqs, &ci.MetadataInput))
 | 
				
			||||||
			check(survey.Ask(cqs, &ci))
 | 
								check(survey.Ask(cqs, &ci))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								keypass, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var newpass bool
 | 
								var newpass bool
 | 
				
			||||||
			prompt := &survey.Confirm{Message: "Do you want a random password?", Default: true}
 | 
								prompt := &survey.Confirm{Message: "Do you want a random password?", Default: true}
 | 
				
			||||||
			check(survey.AskOne(prompt, &newpass, nil))
 | 
								check(survey.AskOne(prompt, &newpass, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if newpass {
 | 
								if newpass {
 | 
				
			||||||
				ci.Password = generatePassword(16, true, true)
 | 
									ci.Password = crypto.GeneratePassword(int(length), numbers, specials)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				var copypass bool
 | 
									var copypass bool
 | 
				
			||||||
				prompt = &survey.Confirm{Message: "Copy new pass to clipboard?", Default: true}
 | 
									prompt = &survey.Confirm{Message: "Copy new pass to clipboard?", Default: true}
 | 
				
			||||||
| 
						 | 
					@ -79,7 +87,7 @@ password.`,
 | 
				
			||||||
				check(survey.AskOne(prompt, &ci.Password, nil))
 | 
									check(survey.AskOne(prompt, &ci.Password, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				var cpass string
 | 
									var cpass string
 | 
				
			||||||
				prompt = &survey.Password{Message: "Confirm assword: "}
 | 
									prompt = &survey.Password{Message: "Confirm password: "}
 | 
				
			||||||
				check(survey.AskOne(prompt, &cpass, nil))
 | 
									check(survey.AskOne(prompt, &cpass, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if ci.Password != cpass {
 | 
									if ci.Password != cpass {
 | 
				
			||||||
| 
						 | 
					@ -88,59 +96,58 @@ password.`,
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			keypass, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
 | 
					 | 
				
			||||||
			check(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			cipherpass, err := crypto.CBCEncrypt(keypass, []byte(ci.Password))
 | 
								cipherpass, err := crypto.CBCEncrypt(keypass, []byte(ci.Password))
 | 
				
			||||||
			check(err)
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ci.Password = base64.StdEncoding.EncodeToString(cipherpass)
 | 
								ci.Password = base64.StdEncoding.EncodeToString(cipherpass)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var otp bool
 | 
				
			||||||
 | 
								prompt = &survey.Confirm{Message: "Do you have an OTP/MFA secret?", Default: true}
 | 
				
			||||||
 | 
								check(survey.AskOne(prompt, &otp, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if otp {
 | 
				
			||||||
 | 
									var secret string
 | 
				
			||||||
 | 
									prompt := &survey.Password{Message: "OTP secret:"}
 | 
				
			||||||
 | 
									check(survey.AskOne(prompt, &secret, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ciphersecret, err := crypto.CBCEncrypt(keypass, []byte(secret))
 | 
				
			||||||
 | 
									check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ci.OTPSecret = base64.StdEncoding.EncodeToString(ciphersecret)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									var copyotp bool
 | 
				
			||||||
 | 
									prompt2 := &survey.Confirm{Message: "Copy new OTP to clipboard?", Default: true}
 | 
				
			||||||
 | 
									check(survey.AskOne(prompt2, ©otp, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if copyotp {
 | 
				
			||||||
 | 
										otp, err := totp.GenerateCode(secret, time.Now())
 | 
				
			||||||
 | 
										check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										check(clipboard.WriteAll(otp))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
 | 
								ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
 | 
				
			||||||
			defer cancel()
 | 
								defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			c, err := initClient(ctx).Create(ctx, ci)
 | 
								c, err := initClient(ctx).Create(ctx, ci)
 | 
				
			||||||
			check(err)
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			mdjson, err := json.MarshalIndent(c.Metadata, "", "  ")
 | 
								fmt.Println(c)
 | 
				
			||||||
			check(err)
 | 
					
 | 
				
			||||||
			fmt.Println(string(mdjson))
 | 
								var cleancb bool
 | 
				
			||||||
 | 
								prompt = &survey.Confirm{Message: "Do you want to clear the clipboard?", Default: true}
 | 
				
			||||||
 | 
								check(survey.AskOne(prompt, &cleancb, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if cleancb {
 | 
				
			||||||
 | 
									check(clipboard.WriteAll(" "))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						createCmd.Flags().BoolVarP(&numbers, "numbers", "n", true, "use numbers in the generated password")
 | 
				
			||||||
 | 
						createCmd.Flags().BoolVarP(&specials, "specials", "s", false, "use special characters in the generated password")
 | 
				
			||||||
 | 
						createCmd.Flags().UintVarP(&length, "length", "l", 32, "length of the generated password")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return createCmd
 | 
						return createCmd
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
const alphas = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV"
 | 
					 | 
				
			||||||
const alphanumerics = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV1234567890"
 | 
					 | 
				
			||||||
const alphasAndSpecials = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV1234567890!@#$%^&*()"
 | 
					 | 
				
			||||||
const alphanumericsAndSpecials = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV1234567890!@#$%^&*()"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func generatePassword(length int, numbers, specials bool) string {
 | 
					 | 
				
			||||||
	rand.Seed(time.Now().UnixNano())
 | 
					 | 
				
			||||||
	pass := make([]byte, length)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch {
 | 
					 | 
				
			||||||
	case numbers && specials:
 | 
					 | 
				
			||||||
		for idx := 0; idx < length; idx++ {
 | 
					 | 
				
			||||||
			pass[idx] = alphanumericsAndSpecials[rand.Int63()%int64(len(alphanumericsAndSpecials))]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case numbers:
 | 
					 | 
				
			||||||
		for idx := 0; idx < length; idx++ {
 | 
					 | 
				
			||||||
			pass[idx] = alphanumerics[rand.Int63()%int64(len(alphanumerics))]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case specials:
 | 
					 | 
				
			||||||
		for idx := 0; idx < length; idx++ {
 | 
					 | 
				
			||||||
			pass[idx] = alphasAndSpecials[rand.Int63()%int64(len(alphasAndSpecials))]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		for idx := 0; idx < length; idx++ {
 | 
					 | 
				
			||||||
			pass[idx] = alphas[rand.Int63()%int64(len(alphas))]
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return string(pass)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										33
									
								
								credentials/commands/delete.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								credentials/commands/delete.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"gopkg.in/AlecAivazis/survey.v1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func MakeDelete(initConfig CredentialClientInit) *cobra.Command {
 | 
				
			||||||
 | 
						deleteCmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "delete [id]",
 | 
				
			||||||
 | 
							Short: "Delete a credential using the given ID",
 | 
				
			||||||
 | 
							Long:  `Delete a credential using the given ID, permanently. THERE IS NO UNDOING THIS ACTION.`,
 | 
				
			||||||
 | 
							Args:  cobra.ExactArgs(1),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Run: func(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
								var confirmed bool
 | 
				
			||||||
 | 
								prompt := &survey.Confirm{Message: "Are you sure you want to permanently delete this credential?"}
 | 
				
			||||||
 | 
								check(survey.AskOne(prompt, &confirmed, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if confirmed {
 | 
				
			||||||
 | 
									ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
 | 
				
			||||||
 | 
									defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									check(initConfig(ctx).Delete(ctx, args[0]))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return deleteCmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										92
									
								
								credentials/commands/get.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								credentials/commands/get.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,92 @@
 | 
				
			||||||
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/atotto/clipboard"
 | 
				
			||||||
 | 
						"github.com/pquerna/otp/totp"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"github.com/spf13/viper"
 | 
				
			||||||
 | 
						"gopkg.in/AlecAivazis/survey.v1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mitchell/selfpass/crypto"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func MakeGet(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
 | 
				
			||||||
 | 
						getCmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "get [id]",
 | 
				
			||||||
 | 
							Short: "Get a credential info and copy password to clipboard",
 | 
				
			||||||
 | 
							Long: `Get a credential's info and copy password to clipboard, from Selfpass server, after
 | 
				
			||||||
 | 
					decrypting password.`,
 | 
				
			||||||
 | 
							Args: cobra.ExactArgs(1),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Run: func(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
								ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
 | 
				
			||||||
 | 
								defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								cred, err := initClient(ctx).Get(ctx, args[0])
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Println(cred)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								check(clipboard.WriteAll(string(cred.Primary)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Println("Wrote primary user key to clipboard.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), key)
 | 
				
			||||||
 | 
								check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var copyPass bool
 | 
				
			||||||
 | 
								prompt := &survey.Confirm{Message: "Copy password to clipboard?", Default: true}
 | 
				
			||||||
 | 
								check(survey.AskOne(prompt, ©Pass, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if copyPass {
 | 
				
			||||||
 | 
									passbytes, err := base64.StdEncoding.DecodeString(cred.Password)
 | 
				
			||||||
 | 
									check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									plainpass, err := crypto.CBCDecrypt(passkey, passbytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									check(clipboard.WriteAll(string(plainpass)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									fmt.Println("Wrote password to clipboard.")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if cred.OTPSecret != "" {
 | 
				
			||||||
 | 
									var newOTP bool
 | 
				
			||||||
 | 
									prompt = &survey.Confirm{Message: "Generate one time password and copy to clipboard?", Default: true}
 | 
				
			||||||
 | 
									check(survey.AskOne(prompt, &newOTP, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if newOTP {
 | 
				
			||||||
 | 
										secretbytes, err := base64.StdEncoding.DecodeString(cred.OTPSecret)
 | 
				
			||||||
 | 
										check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										plainsecret, err := crypto.CBCDecrypt(passkey, secretbytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										otp, err := totp.GenerateCode(string(plainsecret), time.Now())
 | 
				
			||||||
 | 
										check(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										check(clipboard.WriteAll(otp))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										fmt.Println("Wrote one time password to clipboard.")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var cleancb bool
 | 
				
			||||||
 | 
								prompt = &survey.Confirm{Message: "Do you want to clear the clipboard?", Default: true}
 | 
				
			||||||
 | 
								check(survey.AskOne(prompt, &cleancb, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if cleancb {
 | 
				
			||||||
 | 
									check(clipboard.WriteAll(" "))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return getCmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,14 @@
 | 
				
			||||||
package cmds
 | 
					package commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func MakeListCmd(initClient CredentialClientInit) *cobra.Command {
 | 
					func MakeList(initClient CredentialClientInit) *cobra.Command {
 | 
				
			||||||
	var sourceHost string
 | 
						var sourceHost string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	listCmd := &cobra.Command{
 | 
						listCmd := &cobra.Command{
 | 
				
			||||||
| 
						 | 
					@ -38,11 +37,11 @@ includes almost all the information but the most sensitive.`,
 | 
				
			||||||
						break receive
 | 
											break receive
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					mdjson, err := json.MarshalIndent(md, "", "  ")
 | 
										fmt.Println(md)
 | 
				
			||||||
					check(err)
 | 
					 | 
				
			||||||
					fmt.Println(string(mdjson))
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Println("Done listing.")
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -357,6 +357,7 @@ type Credential struct {
 | 
				
			||||||
	SourceHost           string               `protobuf:"bytes,8,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
 | 
						SourceHost           string               `protobuf:"bytes,8,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
 | 
				
			||||||
	LoginUrl             string               `protobuf:"bytes,9,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
 | 
						LoginUrl             string               `protobuf:"bytes,9,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
 | 
				
			||||||
	Tag                  string               `protobuf:"bytes,10,opt,name=tag,proto3" json:"tag,omitempty"`
 | 
						Tag                  string               `protobuf:"bytes,10,opt,name=tag,proto3" json:"tag,omitempty"`
 | 
				
			||||||
 | 
						OtpSecret            string               `protobuf:"bytes,11,opt,name=otp_secret,json=otpSecret,proto3" json:"otp_secret,omitempty"`
 | 
				
			||||||
	XXX_NoUnkeyedLiteral struct{}             `json:"-"`
 | 
						XXX_NoUnkeyedLiteral struct{}             `json:"-"`
 | 
				
			||||||
	XXX_unrecognized     []byte               `json:"-"`
 | 
						XXX_unrecognized     []byte               `json:"-"`
 | 
				
			||||||
	XXX_sizecache        int32                `json:"-"`
 | 
						XXX_sizecache        int32                `json:"-"`
 | 
				
			||||||
| 
						 | 
					@ -457,6 +458,13 @@ func (m *Credential) GetTag() string {
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *Credential) GetOtpSecret() string {
 | 
				
			||||||
 | 
						if m != nil {
 | 
				
			||||||
 | 
							return m.OtpSecret
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CredentialRequest struct {
 | 
					type CredentialRequest struct {
 | 
				
			||||||
	Primary              string   `protobuf:"bytes,1,opt,name=primary,proto3" json:"primary,omitempty"`
 | 
						Primary              string   `protobuf:"bytes,1,opt,name=primary,proto3" json:"primary,omitempty"`
 | 
				
			||||||
	Username             string   `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
 | 
						Username             string   `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
 | 
				
			||||||
| 
						 | 
					@ -465,6 +473,7 @@ type CredentialRequest struct {
 | 
				
			||||||
	SourceHost           string   `protobuf:"bytes,5,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
 | 
						SourceHost           string   `protobuf:"bytes,5,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
 | 
				
			||||||
	LoginUrl             string   `protobuf:"bytes,6,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
 | 
						LoginUrl             string   `protobuf:"bytes,6,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
 | 
				
			||||||
	Tag                  string   `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"`
 | 
						Tag                  string   `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"`
 | 
				
			||||||
 | 
						OtpSecret            string   `protobuf:"bytes,8,opt,name=otp_secret,json=otpSecret,proto3" json:"otp_secret,omitempty"`
 | 
				
			||||||
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 | 
						XXX_NoUnkeyedLiteral struct{} `json:"-"`
 | 
				
			||||||
	XXX_unrecognized     []byte   `json:"-"`
 | 
						XXX_unrecognized     []byte   `json:"-"`
 | 
				
			||||||
	XXX_sizecache        int32    `json:"-"`
 | 
						XXX_sizecache        int32    `json:"-"`
 | 
				
			||||||
| 
						 | 
					@ -544,6 +553,13 @@ func (m *CredentialRequest) GetTag() string {
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *CredentialRequest) GetOtpSecret() string {
 | 
				
			||||||
 | 
						if m != nil {
 | 
				
			||||||
 | 
							return m.OtpSecret
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	proto.RegisterType((*DeleteResponse)(nil), "selfpass.credentials.DeleteResponse")
 | 
						proto.RegisterType((*DeleteResponse)(nil), "selfpass.credentials.DeleteResponse")
 | 
				
			||||||
	proto.RegisterType((*GetAllMetadataRequest)(nil), "selfpass.credentials.GetAllMetadataRequest")
 | 
						proto.RegisterType((*GetAllMetadataRequest)(nil), "selfpass.credentials.GetAllMetadataRequest")
 | 
				
			||||||
| 
						 | 
					@ -559,41 +575,43 @@ func init() {
 | 
				
			||||||
func init() { proto.RegisterFile("credentials/protobuf/service.proto", fileDescriptor_ad34efc7bbd96e69) }
 | 
					func init() { proto.RegisterFile("credentials/protobuf/service.proto", fileDescriptor_ad34efc7bbd96e69) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var fileDescriptor_ad34efc7bbd96e69 = []byte{
 | 
					var fileDescriptor_ad34efc7bbd96e69 = []byte{
 | 
				
			||||||
	// 535 bytes of a gzipped FileDescriptorProto
 | 
						// 561 bytes of a gzipped FileDescriptorProto
 | 
				
			||||||
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0xcf, 0x6e, 0xd3, 0x40,
 | 
						0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x95, 0x4d, 0x6f, 0xd3, 0x4c,
 | 
				
			||||||
	0x10, 0xc6, 0xe5, 0xa4, 0x4d, 0xed, 0x69, 0x88, 0x60, 0x55, 0x24, 0xcb, 0x95, 0x48, 0x64, 0x90,
 | 
						0x10, 0xc7, 0xe5, 0xa4, 0x4d, 0xed, 0x69, 0x9f, 0xe8, 0x61, 0x55, 0x24, 0xcb, 0x15, 0x24, 0x32,
 | 
				
			||||||
	0xa8, 0x8a, 0xe4, 0xa2, 0x72, 0x81, 0x63, 0x68, 0x51, 0xca, 0x01, 0x21, 0xb9, 0xf4, 0xc2, 0x25,
 | 
						0x48, 0x54, 0x45, 0x72, 0x51, 0xb9, 0xc0, 0x31, 0xb4, 0x28, 0xe5, 0x80, 0x90, 0x1c, 0x7a, 0xe1,
 | 
				
			||||||
	0xda, 0xda, 0xd3, 0xd4, 0x92, 0xed, 0x35, 0xbb, 0x63, 0x50, 0x1f, 0x8c, 0x97, 0x80, 0x37, 0xe2,
 | 
						0x12, 0x6d, 0xed, 0x69, 0x6a, 0xc9, 0xf6, 0x9a, 0xdd, 0x31, 0xa8, 0x1f, 0x8c, 0x0f, 0x05, 0x1f,
 | 
				
			||||||
	0x84, 0xbc, 0xfe, 0x93, 0x3f, 0x75, 0x9a, 0x48, 0x5c, 0xb8, 0xed, 0xac, 0xbf, 0x59, 0xcd, 0xef,
 | 
						0x81, 0x13, 0xf2, 0xfa, 0x25, 0x2f, 0x75, 0x9a, 0x48, 0x5c, 0xb8, 0xed, 0x8c, 0xff, 0xb3, 0x9a,
 | 
				
			||||||
	0xdb, 0xf5, 0x07, 0x6e, 0x20, 0x31, 0xc4, 0x94, 0x22, 0x1e, 0xab, 0x93, 0x4c, 0x0a, 0x12, 0xd7,
 | 
						0xdf, 0xec, 0xee, 0xdf, 0xe0, 0x06, 0x12, 0x43, 0x4c, 0x29, 0xe2, 0xb1, 0x3a, 0xcd, 0xa4, 0x20,
 | 
				
			||||||
	0xf9, 0xcd, 0x89, 0x42, 0xf9, 0x3d, 0x0a, 0xd0, 0xd3, 0x1b, 0xec, 0x40, 0x61, 0x7c, 0x93, 0x71,
 | 
						0x71, 0x9d, 0xdf, 0x9c, 0x2a, 0x94, 0xdf, 0xa2, 0x00, 0x3d, 0x9d, 0x60, 0x87, 0x0a, 0xe3, 0x9b,
 | 
				
			||||||
	0xa5, 0xbc, 0x05, 0xb1, 0x33, 0x9c, 0x09, 0x31, 0x8b, 0x71, 0xde, 0x44, 0x51, 0x82, 0x8a, 0x78,
 | 
						0x8c, 0x2b, 0xe5, 0x2d, 0x88, 0x9d, 0xc1, 0x4c, 0x88, 0x59, 0x8c, 0xf3, 0x22, 0x8a, 0x12, 0x54,
 | 
				
			||||||
	0x92, 0x95, 0x6d, 0xee, 0x31, 0x0c, 0xce, 0x31, 0x46, 0x42, 0x1f, 0x55, 0x26, 0x52, 0x85, 0xcc,
 | 
						0xc4, 0x93, 0xac, 0x2c, 0x73, 0x4f, 0xa0, 0x7f, 0x81, 0x31, 0x12, 0xfa, 0xa8, 0x32, 0x91, 0x2a,
 | 
				
			||||||
	0x86, 0x3d, 0x95, 0x07, 0x01, 0x2a, 0x65, 0x1b, 0x23, 0xe3, 0xc8, 0xf4, 0xeb, 0xd2, 0x7d, 0x0b,
 | 
						0x64, 0x36, 0xec, 0xa9, 0x3c, 0x08, 0x50, 0x29, 0xdb, 0x18, 0x1a, 0xc7, 0xa6, 0x5f, 0x87, 0xee,
 | 
				
			||||||
	0x4f, 0x27, 0x48, 0xe3, 0x38, 0xfe, 0x84, 0xc4, 0x43, 0x4e, 0xdc, 0xc7, 0x6f, 0x39, 0x2a, 0x62,
 | 
						0x1b, 0x78, 0x3c, 0x46, 0x1a, 0xc5, 0xf1, 0x47, 0x24, 0x1e, 0x72, 0xe2, 0x3e, 0x7e, 0xcd, 0x51,
 | 
				
			||||||
	0x43, 0xd8, 0x57, 0x22, 0x97, 0x01, 0x4e, 0x6f, 0x85, 0x22, 0xdd, 0x66, 0xf9, 0x50, 0x6e, 0x5d,
 | 
						0x11, 0x1b, 0xc0, 0xbe, 0x12, 0xb9, 0x0c, 0x70, 0x7a, 0x2b, 0x14, 0xe9, 0x32, 0xcb, 0x87, 0x32,
 | 
				
			||||||
	0x08, 0x45, 0xee, 0x21, 0x58, 0x1f, 0xc3, 0x5a, 0x3d, 0x80, 0x4e, 0x14, 0x56, 0xa2, 0x4e, 0x14,
 | 
						0x75, 0x29, 0x14, 0xb9, 0x47, 0x60, 0x7d, 0x08, 0x6b, 0x75, 0x1f, 0x3a, 0x51, 0x58, 0x89, 0x3a,
 | 
				
			||||||
	0xba, 0xb7, 0xf0, 0xe8, 0x2a, 0x0b, 0x79, 0x31, 0x42, 0xab, 0x80, 0x4d, 0x00, 0xe6, 0x4c, 0x76,
 | 
						0x51, 0xe8, 0xde, 0xc2, 0x7f, 0x57, 0x59, 0xc8, 0x8b, 0x16, 0x5a, 0x05, 0x6c, 0x0c, 0x30, 0x67,
 | 
				
			||||||
	0x67, 0x64, 0x1c, 0xed, 0x9f, 0xbe, 0xf4, 0xda, 0x78, 0xbd, 0xb3, 0x66, 0x5d, 0x1d, 0xe6, 0x2f,
 | 
						0xb2, 0x3b, 0x43, 0xe3, 0x78, 0xff, 0xec, 0x85, 0xd7, 0xc6, 0xeb, 0x9d, 0x37, 0xeb, 0x6a, 0x33,
 | 
				
			||||||
	0xb4, 0xba, 0xc7, 0xd0, 0x3f, 0xcf, 0x93, 0xac, 0x41, 0x75, 0xc0, 0x0c, 0x44, 0x4a, 0x98, 0x52,
 | 
						0x7f, 0xa1, 0xd4, 0x3d, 0x81, 0x83, 0x8b, 0x3c, 0xc9, 0x1a, 0x54, 0x07, 0xcc, 0x40, 0xa4, 0x84,
 | 
				
			||||||
	0xc9, 0xda, 0xf7, 0x9b, 0xda, 0x1d, 0x40, 0xff, 0x43, 0x92, 0xd1, 0x5d, 0x75, 0x8e, 0xfb, 0xc7,
 | 
						0x29, 0x95, 0xac, 0x07, 0x7e, 0x13, 0xbb, 0x7d, 0x38, 0x78, 0x9f, 0x64, 0x74, 0x57, 0xed, 0xe3,
 | 
				
			||||||
	0x00, 0xb3, 0xe6, 0xbe, 0x37, 0xe1, 0x3b, 0x3d, 0x21, 0x27, 0x0c, 0xa7, 0x9c, 0xaa, 0x09, 0x1d,
 | 
						0xfe, 0x36, 0xc0, 0xac, 0xb9, 0xef, 0x75, 0xf8, 0x56, 0x77, 0xc8, 0x09, 0xc3, 0x29, 0xa7, 0xaa,
 | 
				
			||||||
	0xaf, 0xf4, 0xde, 0xab, 0xbd, 0xf7, 0xbe, 0xd4, 0xde, 0xfb, 0x56, 0xa5, 0x1e, 0x53, 0xd1, 0x9a,
 | 
						0x43, 0xc7, 0x2b, 0x67, 0xef, 0xd5, 0xb3, 0xf7, 0x3e, 0xd7, 0xb3, 0xf7, 0xad, 0x4a, 0x3d, 0xa2,
 | 
				
			||||||
	0x6b, 0x7a, 0xdd, 0xda, 0xdd, 0xdc, 0x5a, 0xa9, 0xc7, 0x54, 0xdc, 0x54, 0x26, 0xa3, 0x84, 0xcb,
 | 
						0xa2, 0x34, 0xd7, 0xf4, 0xba, 0xb4, 0xbb, 0xb9, 0xb4, 0x52, 0x8f, 0xa8, 0x38, 0xa9, 0x4c, 0x46,
 | 
				
			||||||
	0x3b, 0x7b, 0x47, 0x8f, 0x52, 0x97, 0xab, 0x17, 0xb2, 0xbb, 0x7a, 0x21, 0xec, 0x10, 0xac, 0x58,
 | 
						0x09, 0x97, 0x77, 0xf6, 0x8e, 0x6e, 0xa5, 0x0e, 0x57, 0x0f, 0x64, 0x77, 0xf5, 0x40, 0xd8, 0x11,
 | 
				
			||||||
	0xcc, 0xa2, 0x74, 0x9a, 0xcb, 0xd8, 0xee, 0xe9, 0xcf, 0xa6, 0xde, 0xb8, 0x92, 0x31, 0x7b, 0x0c,
 | 
						0x58, 0xb1, 0x98, 0x45, 0xe9, 0x34, 0x97, 0xb1, 0xdd, 0xd3, 0x9f, 0x4d, 0x9d, 0xb8, 0x92, 0x31,
 | 
				
			||||||
	0x5d, 0xe2, 0x33, 0x7b, 0x4f, 0x6f, 0x17, 0x4b, 0xf7, 0x57, 0x07, 0x60, 0x6e, 0xed, 0x7f, 0x8f,
 | 
						0xfb, 0x1f, 0xba, 0xc4, 0x67, 0xf6, 0x9e, 0x4e, 0x17, 0x4b, 0xf7, 0x67, 0x07, 0x60, 0x3e, 0xda,
 | 
				
			||||||
	0xef, 0x80, 0x99, 0x2b, 0x94, 0x29, 0x4f, 0xb0, 0x62, 0x6f, 0x6a, 0x76, 0x00, 0xbb, 0x98, 0xf0,
 | 
						0x7f, 0x1e, 0xdf, 0x01, 0x33, 0x57, 0x28, 0x53, 0x9e, 0x60, 0xc5, 0xde, 0xc4, 0xec, 0x10, 0x76,
 | 
				
			||||||
	0xa8, 0xa6, 0x2e, 0x8b, 0xa2, 0xa3, 0x78, 0x4b, 0x3f, 0x84, 0x0c, 0x2b, 0xee, 0xa6, 0x5e, 0x35,
 | 
						0x31, 0xe1, 0x51, 0x4d, 0x5d, 0x06, 0x45, 0x45, 0x71, 0x97, 0xbe, 0x0b, 0x19, 0x56, 0xdc, 0x4d,
 | 
				
			||||||
	0xd3, 0x7c, 0xd8, 0x4c, 0xab, 0xdd, 0x4c, 0x98, 0x9b, 0xf9, 0xdb, 0x80, 0x27, 0xf7, 0xde, 0xe9,
 | 
						0xbc, 0x3a, 0x4c, 0xf3, 0xe1, 0x61, 0x5a, 0xed, 0xc3, 0x84, 0x66, 0x98, 0xec, 0x09, 0x80, 0xa0,
 | 
				
			||||||
	0x22, 0x8d, 0xb1, 0x9e, 0xa6, 0xb3, 0x8e, 0xa6, 0xbb, 0x8e, 0x66, 0xe7, 0x61, 0x9a, 0x7f, 0x7d,
 | 
						0x6c, 0xaa, 0x30, 0x90, 0x48, 0xf6, 0xbe, 0xfe, 0x60, 0x09, 0xca, 0x26, 0x3a, 0xe1, 0xfe, 0x32,
 | 
				
			||||||
	0x1a, 0xa7, 0x3f, 0xbb, 0x8b, 0x34, 0x97, 0x65, 0x26, 0xb1, 0x29, 0x0c, 0x96, 0xa3, 0x82, 0xbd,
 | 
						0xe0, 0xd1, 0xbd, 0x6b, 0xbc, 0x08, 0x6b, 0xac, 0x87, 0xed, 0xac, 0x83, 0xed, 0xae, 0x83, 0xdd,
 | 
				
			||||||
	0x6a, 0xff, 0x61, 0x5b, 0x03, 0xc5, 0x79, 0xd6, 0x2e, 0xae, 0x65, 0xaf, 0x0d, 0x76, 0x01, 0xdd,
 | 
						0x79, 0x18, 0xf6, 0x6f, 0x6f, 0xce, 0x0a, 0xac, 0xb9, 0x02, 0x7b, 0xf6, 0xa3, 0xbb, 0x08, 0x3b,
 | 
				
			||||||
	0x09, 0x12, 0x1b, 0xb6, 0x0b, 0x9b, 0xb0, 0x71, 0x46, 0x9b, 0x72, 0x82, 0x5d, 0x42, 0xef, 0x4c,
 | 
						0x29, 0x1d, 0x8d, 0x4d, 0xa1, 0xbf, 0x6c, 0x34, 0xec, 0x65, 0xfb, 0x73, 0x6f, 0xb5, 0x23, 0xe7,
 | 
				
			||||||
	0x3f, 0x47, 0xb6, 0x6d, 0xa6, 0x6c, 0x71, 0xe8, 0x67, 0xe8, 0x95, 0x99, 0xc6, 0x9e, 0xb7, 0x6b,
 | 
						0x69, 0xbb, 0xb8, 0x96, 0xbd, 0x32, 0xd8, 0x25, 0x74, 0xc7, 0x48, 0x6c, 0xd0, 0x2e, 0x6c, 0xac,
 | 
				
			||||||
	0x97, 0x12, 0x6f, 0xbb, 0x03, 0xcb, 0x9c, 0xde, 0x8c, 0xfc, 0xa2, 0x5d, 0xb0, 0x1c, 0xf3, 0xef,
 | 
						0xca, 0x19, 0x6e, 0x72, 0x19, 0x36, 0x81, 0xde, 0xb9, 0xbe, 0xcc, 0x6c, 0x5b, 0x47, 0xda, 0x62,
 | 
				
			||||||
	0xe1, 0xab, 0x59, 0xff, 0x5e, 0xd7, 0x3d, 0xbd, 0x7a, 0xf3, 0x37, 0x00, 0x00, 0xff, 0xff, 0x22,
 | 
						0xd3, 0x4f, 0xd0, 0x2b, 0x1d, 0x91, 0x3d, 0x6b, 0xd7, 0x2e, 0xf9, 0xe5, 0x76, 0x1b, 0x96, 0x2e,
 | 
				
			||||||
	0xb6, 0x48, 0x2d, 0x68, 0x06, 0x00, 0x00,
 | 
						0xbf, 0x19, 0xf9, 0x79, 0xbb, 0x60, 0xf9, 0x27, 0xf1, 0x0e, 0xbe, 0x98, 0xf5, 0xe3, 0xbc, 0xee,
 | 
				
			||||||
 | 
						0xe9, 0xd5, 0xeb, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xde, 0x03, 0x4f, 0xb2, 0xa6, 0x06, 0x00,
 | 
				
			||||||
 | 
						0x00,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Reference imports to suppress errors if they are not otherwise used.
 | 
					// Reference imports to suppress errors if they are not otherwise used.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,6 +60,7 @@ message Credential {
 | 
				
			||||||
        string source_host = 8;
 | 
					        string source_host = 8;
 | 
				
			||||||
        string login_url = 9;
 | 
					        string login_url = 9;
 | 
				
			||||||
        string tag = 10;
 | 
					        string tag = 10;
 | 
				
			||||||
 | 
					        string otp_secret = 11;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message CredentialRequest {
 | 
					message CredentialRequest {
 | 
				
			||||||
| 
						 | 
					@ -70,4 +71,5 @@ message CredentialRequest {
 | 
				
			||||||
        string source_host = 5;
 | 
					        string source_host = 5;
 | 
				
			||||||
        string login_url = 6;
 | 
					        string login_url = 6;
 | 
				
			||||||
        string tag = 7;
 | 
					        string tag = 7;
 | 
				
			||||||
 | 
					        string otp_secret = 8;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,12 +7,13 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"google.golang.org/grpc"
 | 
				
			||||||
 | 
						"google.golang.org/grpc/credentials"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/endpoints"
 | 
						"github.com/mitchell/selfpass/credentials/endpoints"
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/protobuf"
 | 
						"github.com/mitchell/selfpass/credentials/protobuf"
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/transport"
 | 
						"github.com/mitchell/selfpass/credentials/transport"
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/types"
 | 
						"github.com/mitchell/selfpass/credentials/types"
 | 
				
			||||||
	"google.golang.org/grpc"
 | 
					 | 
				
			||||||
	"google.golang.org/grpc/credentials"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewCredentialServiceClient(ctx context.Context, target, ca, cert, key string) (types.CredentialClient, error) {
 | 
					func NewCredentialServiceClient(ctx context.Context, target, ca, cert, key string) (types.CredentialClient, error) {
 | 
				
			||||||
| 
						 | 
					@ -34,16 +35,16 @@ func NewCredentialServiceClient(ctx context.Context, target, ca, cert, key strin
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CredentialServiceClient{
 | 
						return credentialServiceClient{
 | 
				
			||||||
		client: protobuf.NewCredentialServiceClient(conn),
 | 
							client: protobuf.NewCredentialServiceClient(conn),
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CredentialServiceClient struct {
 | 
					type credentialServiceClient struct {
 | 
				
			||||||
	client protobuf.CredentialServiceClient
 | 
						client protobuf.CredentialServiceClient
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c CredentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
 | 
					func (c credentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
 | 
				
			||||||
	pbmdch := make(chan protobuf.Metadata, 1)
 | 
						pbmdch := make(chan protobuf.Metadata, 1)
 | 
				
			||||||
	errch = make(chan error, 1)
 | 
						errch = make(chan error, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,7 +85,7 @@ func (c CredentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost
 | 
				
			||||||
	return stream.Metadata, stream.Errors
 | 
						return stream.Metadata, stream.Errors
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c CredentialServiceClient) Get(ctx context.Context, id string) (output types.Credential, err error) {
 | 
					func (c credentialServiceClient) Get(ctx context.Context, id string) (output types.Credential, err error) {
 | 
				
			||||||
	req := transport.EncodeIdRequest(endpoints.IDRequest{ID: id})
 | 
						req := transport.EncodeIdRequest(endpoints.IDRequest{ID: id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := c.client.Get(ctx, &req)
 | 
						res, err := c.client.Get(ctx, &req)
 | 
				
			||||||
| 
						 | 
					@ -95,7 +96,7 @@ func (c CredentialServiceClient) Get(ctx context.Context, id string) (output typ
 | 
				
			||||||
	return transport.DecodeCredential(*res)
 | 
						return transport.DecodeCredential(*res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c CredentialServiceClient) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
 | 
					func (c credentialServiceClient) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
 | 
				
			||||||
	req := transport.EncodeCredentialRequest(ci)
 | 
						req := transport.EncodeCredentialRequest(ci)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := c.client.Create(ctx, &req)
 | 
						res, err := c.client.Create(ctx, &req)
 | 
				
			||||||
| 
						 | 
					@ -106,10 +107,26 @@ func (c CredentialServiceClient) Create(ctx context.Context, ci types.Credential
 | 
				
			||||||
	return transport.DecodeCredential(*res)
 | 
						return transport.DecodeCredential(*res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c CredentialServiceClient) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
 | 
					func (c credentialServiceClient) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
 | 
				
			||||||
	panic("implement me")
 | 
						req := transport.EncodeUpdateRequest(endpoints.UpdateRequest{ID: id, Credential: ci})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := c.client.Update(ctx, &req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return output, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return transport.DecodeCredential(*res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c CredentialServiceClient) Delete(ctx context.Context, id string) (err error) {
 | 
					func (c credentialServiceClient) Delete(ctx context.Context, id string) (err error) {
 | 
				
			||||||
	panic("implement me")
 | 
						req := transport.EncodeIdRequest(endpoints.IDRequest{ID: id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := c.client.Delete(ctx, &req)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !res.Success {
 | 
				
			||||||
 | 
							return fmt.Errorf("delete unsuccessful")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,7 @@ func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (ou
 | 
				
			||||||
	c.Username = ci.Username
 | 
						c.Username = ci.Username
 | 
				
			||||||
	c.Email = ci.Email
 | 
						c.Email = ci.Email
 | 
				
			||||||
	c.Password = ci.Password
 | 
						c.Password = ci.Password
 | 
				
			||||||
 | 
						c.OTPSecret = ci.OTPSecret
 | 
				
			||||||
	c.Tag = ci.Tag
 | 
						c.Tag = ci.Tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = svc.repo.Put(ctx, c)
 | 
						err = svc.repo.Put(ctx, c)
 | 
				
			||||||
| 
						 | 
					@ -97,6 +98,7 @@ func (svc Credentials) Update(ctx context.Context, id string, ci types.Credentia
 | 
				
			||||||
	c.LoginURL = ci.LoginURL
 | 
						c.LoginURL = ci.LoginURL
 | 
				
			||||||
	c.SourceHost = ci.SourceHost
 | 
						c.SourceHost = ci.SourceHost
 | 
				
			||||||
	c.Password = ci.Password
 | 
						c.Password = ci.Password
 | 
				
			||||||
 | 
						c.OTPSecret = ci.OTPSecret
 | 
				
			||||||
	c.Email = ci.Email
 | 
						c.Email = ci.Email
 | 
				
			||||||
	c.Username = ci.Username
 | 
						c.Username = ci.Username
 | 
				
			||||||
	c.Tag = ci.Tag
 | 
						c.Tag = ci.Tag
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/golang/protobuf/ptypes"
 | 
						"github.com/golang/protobuf/ptypes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/endpoints"
 | 
						"github.com/mitchell/selfpass/credentials/endpoints"
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/protobuf"
 | 
						"github.com/mitchell/selfpass/credentials/protobuf"
 | 
				
			||||||
	"github.com/mitchell/selfpass/credentials/types"
 | 
						"github.com/mitchell/selfpass/credentials/types"
 | 
				
			||||||
| 
						 | 
					@ -121,9 +122,10 @@ func decodeCredentialRequest(ctx context.Context, request interface{}) (interfac
 | 
				
			||||||
			SourceHost: r.SourceHost,
 | 
								SourceHost: r.SourceHost,
 | 
				
			||||||
			Tag:        r.Tag,
 | 
								Tag:        r.Tag,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Username: r.Username,
 | 
							Username:  r.Username,
 | 
				
			||||||
		Email:    r.Email,
 | 
							Email:     r.Email,
 | 
				
			||||||
		Password: r.Password,
 | 
							Password:  r.Password,
 | 
				
			||||||
 | 
							OTPSecret: r.OtpSecret,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -133,6 +135,7 @@ func EncodeCredentialRequest(r types.CredentialInput) protobuf.CredentialRequest
 | 
				
			||||||
		Username:   r.Username,
 | 
							Username:   r.Username,
 | 
				
			||||||
		Email:      r.Email,
 | 
							Email:      r.Email,
 | 
				
			||||||
		Password:   r.Password,
 | 
							Password:   r.Password,
 | 
				
			||||||
 | 
							OtpSecret:  r.OTPSecret,
 | 
				
			||||||
		SourceHost: r.SourceHost,
 | 
							SourceHost: r.SourceHost,
 | 
				
			||||||
		LoginUrl:   r.LoginURL,
 | 
							LoginUrl:   r.LoginURL,
 | 
				
			||||||
		Tag:        r.Tag,
 | 
							Tag:        r.Tag,
 | 
				
			||||||
| 
						 | 
					@ -163,6 +166,7 @@ func encodeCredentialResponse(ctx context.Context, response interface{}) (interf
 | 
				
			||||||
		Username:   r.Username,
 | 
							Username:   r.Username,
 | 
				
			||||||
		Email:      r.Email,
 | 
							Email:      r.Email,
 | 
				
			||||||
		Password:   r.Password,
 | 
							Password:   r.Password,
 | 
				
			||||||
 | 
							OtpSecret:  r.OTPSecret,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,9 +192,10 @@ func DecodeCredential(r protobuf.Credential) (c types.Credential, err error) {
 | 
				
			||||||
			LoginURL:   r.LoginUrl,
 | 
								LoginURL:   r.LoginUrl,
 | 
				
			||||||
			Tag:        r.Tag,
 | 
								Tag:        r.Tag,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Username: r.Username,
 | 
							Username:  r.Username,
 | 
				
			||||||
		Email:    r.Email,
 | 
							Email:     r.Email,
 | 
				
			||||||
		Password: r.Password,
 | 
							Password:  r.Password,
 | 
				
			||||||
 | 
							OTPSecret: r.OtpSecret,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -206,16 +211,15 @@ func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
 | 
				
			||||||
				LoginURL:   r.Credential.LoginUrl,
 | 
									LoginURL:   r.Credential.LoginUrl,
 | 
				
			||||||
				Tag:        r.Credential.Tag,
 | 
									Tag:        r.Credential.Tag,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Username: r.Credential.Username,
 | 
								Username:  r.Credential.Username,
 | 
				
			||||||
			Email:    r.Credential.Email,
 | 
								Email:     r.Credential.Email,
 | 
				
			||||||
			Password: r.Credential.Password,
 | 
								Password:  r.Credential.Password,
 | 
				
			||||||
 | 
								OTPSecret: r.Credential.OtpSecret,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func EncodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
 | 
					func EncodeUpdateRequest(r endpoints.UpdateRequest) protobuf.UpdateRequest {
 | 
				
			||||||
	r := request.(endpoints.UpdateRequest)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	c := r.Credential
 | 
						c := r.Credential
 | 
				
			||||||
	return protobuf.UpdateRequest{
 | 
						return protobuf.UpdateRequest{
 | 
				
			||||||
		Id: r.ID,
 | 
							Id: r.ID,
 | 
				
			||||||
| 
						 | 
					@ -224,11 +228,12 @@ func EncodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
 | 
				
			||||||
			Username:   c.Username,
 | 
								Username:   c.Username,
 | 
				
			||||||
			Email:      c.Email,
 | 
								Email:      c.Email,
 | 
				
			||||||
			Password:   c.Password,
 | 
								Password:   c.Password,
 | 
				
			||||||
 | 
								OtpSecret:  c.OTPSecret,
 | 
				
			||||||
			SourceHost: c.SourceHost,
 | 
								SourceHost: c.SourceHost,
 | 
				
			||||||
			LoginUrl:   c.LoginURL,
 | 
								LoginUrl:   c.LoginURL,
 | 
				
			||||||
			Tag:        c.Tag,
 | 
								Tag:        c.Tag,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}, nil
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, error) {
 | 
					func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, error) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
package types
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,16 +9,25 @@ const TypePrefixCred = "cred"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Credential struct {
 | 
					type Credential struct {
 | 
				
			||||||
	Metadata
 | 
						Metadata
 | 
				
			||||||
	Username string
 | 
						Username  string
 | 
				
			||||||
	Email    string
 | 
						Email     string
 | 
				
			||||||
	Password string `json:"-"`
 | 
						Password  string `json:"-"`
 | 
				
			||||||
 | 
						OTPSecret string `json:"-"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c Credential) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf(
 | 
				
			||||||
 | 
							"username = %s\nemail = %s\n%s",
 | 
				
			||||||
 | 
							c.Username, c.Email, c.Metadata,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CredentialInput struct {
 | 
					type CredentialInput struct {
 | 
				
			||||||
	MetadataInput
 | 
						MetadataInput
 | 
				
			||||||
	Username string
 | 
						Username  string
 | 
				
			||||||
	Email    string
 | 
						Email     string
 | 
				
			||||||
	Password string
 | 
						Password  string
 | 
				
			||||||
 | 
						OTPSecret string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Metadata struct {
 | 
					type Metadata struct {
 | 
				
			||||||
| 
						 | 
					@ -30,6 +40,13 @@ type Metadata struct {
 | 
				
			||||||
	Tag        string
 | 
						Tag        string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m Metadata) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf(
 | 
				
			||||||
 | 
							"id = %s\nsourceHost = %s\ncreatedAt = %s\nupdatedAt = %s\nprimary = %s\nloginUrl = %s\ntag = %s\n",
 | 
				
			||||||
 | 
							m.ID, m.SourceHost, m.CreatedAt, m.UpdatedAt, m.Primary, m.LoginURL, m.Tag,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MetadataInput struct {
 | 
					type MetadataInput struct {
 | 
				
			||||||
	Primary    string
 | 
						Primary    string
 | 
				
			||||||
	SourceHost string
 | 
						SourceHost string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,37 +10,6 @@ import (
 | 
				
			||||||
	"github.com/cloudflare/redoctober/padding"
 | 
						"github.com/cloudflare/redoctober/padding"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GenerateKeyFromPassword(pass []byte) ([]byte, error) {
 | 
					 | 
				
			||||||
	if len(pass) < 8 {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("master password must be at least 8 characters")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for idx := 0; len(pass) < 32; idx++ {
 | 
					 | 
				
			||||||
		pass = append(pass, pass[idx])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if idx == len(pass) {
 | 
					 | 
				
			||||||
			idx = 0
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return pass, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func CombinePasswordAndKey(pass, key []byte) ([]byte, error) {
 | 
					 | 
				
			||||||
	if len(pass) < 8 {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("master password must be at least 8 characters")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(key) != 16 {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("key was not of length 16")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for idx := 0; len(pass) < 16; idx++ {
 | 
					 | 
				
			||||||
		pass = append(pass, pass[idx])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return append(pass[:16], key...), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func CBCEncrypt(key []byte, plaintext []byte) ([]byte, error) {
 | 
					func CBCEncrypt(key []byte, plaintext []byte) ([]byte, error) {
 | 
				
			||||||
	if len(key) != 32 {
 | 
						if len(key) != 32 {
 | 
				
			||||||
		return nil, fmt.Errorf("key is not 32 bytes")
 | 
							return nil, fmt.Errorf("key is not 32 bytes")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								crypto/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								crypto/helpers.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					package crypto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GenerateKeyFromPassword(pass []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						if len(pass) < 8 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("master password must be at least 8 characters")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for idx := 0; len(pass) < 32; idx++ {
 | 
				
			||||||
 | 
							pass = append(pass, pass[idx])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if idx == len(pass) {
 | 
				
			||||||
 | 
								idx = 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pass, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CombinePasswordAndKey(pass, key []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						if len(pass) < 8 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("master password must be at least 8 characters")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(key) != 16 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("key was not of length 16")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for idx := 0; len(pass) < 16; idx++ {
 | 
				
			||||||
 | 
							pass = append(pass, pass[idx])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return append(pass[:16], key...), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GeneratePassword(length int, numbers, specials bool) string {
 | 
				
			||||||
 | 
						const alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
 | 
				
			||||||
 | 
						const alphanumerics = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
 | 
				
			||||||
 | 
						const alphasAndSpecials = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()"
 | 
				
			||||||
 | 
						const alphanumericsAndSpecials = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()"
 | 
				
			||||||
 | 
						rand.Seed(time.Now().UnixNano())
 | 
				
			||||||
 | 
						pass := make([]byte, length)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case numbers && specials:
 | 
				
			||||||
 | 
							for idx := 0; idx < length; idx++ {
 | 
				
			||||||
 | 
								pass[idx] = alphanumericsAndSpecials[rand.Int63()%int64(len(alphanumericsAndSpecials))]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case numbers:
 | 
				
			||||||
 | 
							for idx := 0; idx < length; idx++ {
 | 
				
			||||||
 | 
								pass[idx] = alphanumerics[rand.Int63()%int64(len(alphanumerics))]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case specials:
 | 
				
			||||||
 | 
							for idx := 0; idx < length; idx++ {
 | 
				
			||||||
 | 
								pass[idx] = alphasAndSpecials[rand.Int63()%int64(len(alphasAndSpecials))]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							for idx := 0; idx < length; idx++ {
 | 
				
			||||||
 | 
								pass[idx] = alphas[rand.Int63()%int64(len(alphas))]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return string(pass)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,16 @@
 | 
				
			||||||
version: "3.7"
 | 
					version: "3.7"
 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  redis:
 | 
					  redis:
 | 
				
			||||||
    image: "redis:alpine"
 | 
					    image: "redis:5.0.5"
 | 
				
			||||||
  selfpass:
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - "./data:/data"
 | 
				
			||||||
 | 
					  server:
 | 
				
			||||||
    build: .
 | 
					    build: .
 | 
				
			||||||
    entrypoint:
 | 
					    restart: always
 | 
				
			||||||
      - server
 | 
					 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - "8080:8080"
 | 
					      - "8080:8080"
 | 
				
			||||||
    secrets:
 | 
					    entrypoint:
 | 
				
			||||||
      - ca_cert
 | 
					      - server
 | 
				
			||||||
      - server_cert
 | 
					      - -v
 | 
				
			||||||
      - server_key
 | 
					    depends_on:
 | 
				
			||||||
secrets:
 | 
					      - redis
 | 
				
			||||||
  ca_cert:
 | 
					 | 
				
			||||||
    file: ./certs/ca.pem
 | 
					 | 
				
			||||||
  server_cert:
 | 
					 | 
				
			||||||
    file: ./certs/server.pem
 | 
					 | 
				
			||||||
  server_key:
 | 
					 | 
				
			||||||
    file: ./certs/server-key.pem
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								gen_certs_go.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										13
									
								
								gen_certs_go.sh
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env sh
 | 
				
			||||||
 | 
					ca=$(cat ./certs/ca.pem)
 | 
				
			||||||
 | 
					cert=$(cat ./certs/server.pem)
 | 
				
			||||||
 | 
					key=$(cat ./certs/server-key.pem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cat << EOM
 | 
				
			||||||
 | 
					// Code generated by inject-certs.sh, DO NOT EDIT.
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ca = \`${ca}\`
 | 
				
			||||||
 | 
					const cert = \`${cert}\`
 | 
				
			||||||
 | 
					const key = \`${key}\`
 | 
				
			||||||
 | 
					EOM
 | 
				
			||||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							| 
						 | 
					@ -4,7 +4,9 @@ require (
 | 
				
			||||||
	github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b // indirect
 | 
						github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b // indirect
 | 
				
			||||||
	github.com/atotto/clipboard v0.1.2
 | 
						github.com/atotto/clipboard v0.1.2
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2 v0.7.0
 | 
						github.com/aws/aws-sdk-go-v2 v0.7.0
 | 
				
			||||||
 | 
						github.com/boombuler/barcode v1.0.0 // indirect
 | 
				
			||||||
	github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692
 | 
						github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692
 | 
				
			||||||
 | 
						github.com/davecgh/go-spew v1.1.1
 | 
				
			||||||
	github.com/go-kit/kit v0.8.0
 | 
						github.com/go-kit/kit v0.8.0
 | 
				
			||||||
	github.com/go-logfmt/logfmt v0.4.0 // indirect
 | 
						github.com/go-logfmt/logfmt v0.4.0 // indirect
 | 
				
			||||||
	github.com/go-stack/stack v1.8.0 // indirect
 | 
						github.com/go-stack/stack v1.8.0 // indirect
 | 
				
			||||||
| 
						 | 
					@ -17,8 +19,8 @@ require (
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.7 // indirect
 | 
						github.com/mattn/go-isatty v0.0.7 // indirect
 | 
				
			||||||
	github.com/mediocregopher/radix/v3 v3.2.3
 | 
						github.com/mediocregopher/radix/v3 v3.2.3
 | 
				
			||||||
	github.com/mitchellh/go-homedir v1.1.0
 | 
						github.com/mitchellh/go-homedir v1.1.0
 | 
				
			||||||
 | 
						github.com/pquerna/otp v1.1.0
 | 
				
			||||||
	github.com/spf13/cobra v0.0.3
 | 
						github.com/spf13/cobra v0.0.3
 | 
				
			||||||
	github.com/spf13/pflag v1.0.3
 | 
					 | 
				
			||||||
	github.com/spf13/viper v1.3.2
 | 
						github.com/spf13/viper v1.3.2
 | 
				
			||||||
	github.com/stretchr/testify v1.3.0 // indirect
 | 
						github.com/stretchr/testify v1.3.0 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect
 | 
						golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| 
						 | 
					@ -10,6 +10,8 @@ github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzb
 | 
				
			||||||
github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
 | 
					github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2 v0.7.0 h1:a5xRI/tBmUFKuAA0SOyEY2P1YhQb+jVOEI9P/7KfrP0=
 | 
					github.com/aws/aws-sdk-go-v2 v0.7.0 h1:a5xRI/tBmUFKuAA0SOyEY2P1YhQb+jVOEI9P/7KfrP0=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2 v0.7.0/go.mod h1:17MaCZ9g0q5BIMxwzRQeiv8M3c8+W7iuBnlWAEprcxE=
 | 
					github.com/aws/aws-sdk-go-v2 v0.7.0/go.mod h1:17MaCZ9g0q5BIMxwzRQeiv8M3c8+W7iuBnlWAEprcxE=
 | 
				
			||||||
 | 
					github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc=
 | 
				
			||||||
 | 
					github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
 | 
				
			||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
					github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
				
			||||||
github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692 h1:eVvf+nrm0mXV3JOh2c9vt+Pemh/TAUOjqRNrp1eyPmk=
 | 
					github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692 h1:eVvf+nrm0mXV3JOh2c9vt+Pemh/TAUOjqRNrp1eyPmk=
 | 
				
			||||||
github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
 | 
					github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
 | 
				
			||||||
| 
						 | 
					@ -81,6 +83,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
 | 
				
			||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
 | 
					github.com/pquerna/otp v1.1.0 h1:q2gMsMuMl3JzneUaAX1MRGxLvOG6bzXV51hivBaStf0=
 | 
				
			||||||
 | 
					github.com/pquerna/otp v1.1.0/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk=
 | 
				
			||||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 | 
					github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 | 
				
			||||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 | 
					github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 | 
				
			||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
					github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +0,0 @@
 | 
				
			||||||
grpcurl -cacert ./certs/ca.pem \
 | 
					 | 
				
			||||||
        -cert ./certs/client.pem \
 | 
					 | 
				
			||||||
        -key ./certs/client-key.pem \
 | 
					 | 
				
			||||||
        -proto ./credentials/protobuf/service.proto \
 | 
					 | 
				
			||||||
        localhost:8080 \
 | 
					 | 
				
			||||||
        selfpass.credentials.CredentialService/GetAllMetadata
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in a new issue