Implemented encryption functionality of spc and password generation; refactors on spc and server

This commit is contained in:
mitchell 2019-05-22 08:22:40 -07:00
parent c289eecd54
commit cd24f6e848
26 changed files with 1151 additions and 1522 deletions

View File

@ -1,22 +1,11 @@
FROM golang:1.11.5 as build
WORKDIR /go/src/github.com/mitchell/selfpass
COPY . .
RUN go get -u golang.org/x/tools/cmd/goimports
ENV GO111MODULE=on
RUN make build
FROM debian:stable-20190326-slim
RUN printf "deb http://httpredir.debian.org/debian stretch-backports main non-free\ndeb-src http://httpredir.debian.org/debian stretch-backports main non-free" > /etc/apt/sources.list.d/backports.list
RUN apt-get update && apt-get install -t stretch-backports -y --no-install-recommends redis-server=5:5.0.3-3~bpo9+2 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /usr/bin/selfpass/
WORKDIR /usr/bin
COPY --from=build /go/src/github.com/mitchell/selfpass/bin/server .
COPY --from=build /go/src/github.com/mitchell/selfpass/redis.conf .
COPY --from=build /go/src/github.com/mitchell/selfpass/db/dump.rdb ./db/dump.rdb
COPY --from=build /go/src/github.com/mitchell/selfpass/certs/ca.pem ./certs/ca.pem
COPY --from=build /go/src/github.com/mitchell/selfpass/certs/server.pem ./certs/server.pem
COPY --from=build /go/src/github.com/mitchell/selfpass/certs/server-key.pem ./certs/server-key.pem
COPY --from=build /go/src/github.com/mitchell/selfpass/dual-entry ./dual-entry
ENTRYPOINT ./dual-entry
ENTRYPOINT ["server"]
EXPOSE 8080

View File

@ -1,23 +1,23 @@
.PHONY: all build clean format test docker-build
build: clean format test
go build -o ./bin/server ./cmd/server/server.go
docker-build:
docker build -t selfpass .
go build --o ./bin/server ./cmd/server/server.go
clean:
rm -rf ./bin
go mod tidy
dev: docker-build
docker run -i -t -p 8080:8080 selfpass -v -dev
docker:
docker-compose build
local: docker-build
docker run -i -t -p 8080:8080 selfpass
start:
docker-compose up
format:
goimports -w -l .
gofmt -w -s -l .
install-spc:
go install ./cmd/spc
gen-protoc:
protoc --go_out=plugins=grpc:. \

View File

@ -10,16 +10,18 @@ import (
"net"
"os"
"os/signal"
"syscall"
"github.com/go-kit/kit/log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"github.com/mitchell/selfpass/credentials/middleware"
"github.com/mitchell/selfpass/credentials/protobuf"
"github.com/mitchell/selfpass/credentials/repositories"
"github.com/mitchell/selfpass/credentials/service"
"github.com/mitchell/selfpass/credentials/transport"
"github.com/mitchell/selfpass/credentials/types"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
var logger log.Logger
@ -29,9 +31,9 @@ func main() {
stop = make(chan os.Signal, 1)
dev = flag.Bool("dev", false, "enables dev mode logging")
port = flag.String("port", "8080", "specify the port to listen on")
crtFile = flag.String("cert", "./certs/server.pem", "specify the cert file")
keyFile = flag.String("key", "./certs/server-key.pem", "specify the private key file")
caFile = flag.String("ca", "./certs/ca.pem", "specify the ca cert file")
crtFile = flag.String("cert", "/run/secrets/server_cert", "specify the cert file")
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(
// "credential-table-name",
@ -39,10 +41,12 @@ func main() {
// "specify the credential table name on AWS",
// )
)
signal.Notify(stop, os.Interrupt)
flag.Parse()
signal.Notify(stop, syscall.SIGINT)
signal.Notify(stop, syscall.SIGKILL)
signal.Notify(stop, syscall.SIGTERM)
logger = newLogger(os.Stdout, *dev)
keypair, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
@ -61,9 +65,7 @@ func main() {
})
// db := repositories.NewDynamoTable(*tableName)
db, err := repositories.NewRedisConn(
repositories.ConnConfig{NetworkType: "tcp", Address: "localhost:6379", Size: 2},
)
db, err := repositories.NewRedisConn("tcp", "redis:6379", 2)
check(err)
var svc types.Service

95
cmd/spc/cmd/init.go Normal file
View File

@ -0,0 +1,95 @@
package cmd
import (
"fmt"
"io/ioutil"
"strings"
"github.com/google/uuid"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/credentials/cmds"
)
func makeInitCmd(cfg *viper.Viper) *cobra.Command {
initCmd := &cobra.Command{
Use: "init",
Short: "This command initializes SPC for the first time",
Long: `This command initializes SPC for the first time. Writing to the user configuration
the users private key, and server certificates. (All of which will be encrypted)`,
Run: func(cmd *cobra.Command, args []string) {
var (
hasPK bool
masterpass string
cmasterpass string
target string
caFile string
certFile string
keyFile string
prompt survey.Prompt
privateKey = strings.Replace(uuid.New().String(), "-", "", -1)
)
prompt = &survey.Password{Message: "Master password:"}
check(survey.AskOne(prompt, &masterpass, nil))
prompt = &survey.Password{Message: "Confirm master password:"}
check(survey.AskOne(prompt, &cmasterpass, nil))
if masterpass != cmasterpass {
check(fmt.Errorf("master passwords didn't match"))
}
prompt = &survey.Input{Message: "Selfpass server address:"}
check(survey.AskOne(prompt, &target, nil))
prompt = &survey.Confirm{Message: "Do you have a private key?"}
check(survey.AskOne(prompt, &hasPK, nil))
if hasPK {
prompt = &survey.Input{Message: "Private key:"}
check(survey.AskOne(prompt, &privateKey, nil))
privateKey = strings.Replace(privateKey, "-", "", -1)
}
prompt = &survey.Input{Message: "CA certificate file:"}
check(survey.AskOne(prompt, &caFile, nil))
ca, err := ioutil.ReadFile(caFile)
check(err)
prompt = &survey.Input{Message: "Client certificate file:"}
check(survey.AskOne(prompt, &certFile, nil))
cert, err := ioutil.ReadFile(certFile)
check(err)
prompt = &survey.Input{Message: "Client key file:"}
check(survey.AskOne(prompt, &keyFile, nil))
key, err := ioutil.ReadFile(keyFile)
check(err)
cfg.Set(cmds.KeyConnConfig, map[string]string{
"target": target,
"ca": string(ca),
"cert": string(cert),
"key": string(key),
})
cfg.Set(cmds.KeyPrivateKey, privateKey)
if err := cfg.WriteConfig(); err != nil {
home, err := homedir.Dir()
check(err)
check(cfg.WriteConfigAs(home + "/.spc.toml"))
cfg.SetConfigFile(home + "/.spc.toml")
fmt.Println("Wrote new config to: " + home + "/.spc.toml")
}
encryptConfig(masterpass, cfg)
},
}
return initCmd
}

147
cmd/spc/cmd/root.go Normal file
View File

@ -0,0 +1,147 @@
package cmd
import (
"context"
"fmt"
"io/ioutil"
"os"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/credentials/cmds"
"github.com/mitchell/selfpass/credentials/types"
"github.com/mitchell/selfpass/crypto"
)
func Execute(ctx context.Context, initClient types.CredentialClientInit) {
rootCmd := &cobra.Command{
Use: "spc",
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
can interact with the entire Selfpass API.`,
}
cfgFile := rootCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.spc.toml)")
rootCmd.PersistentFlags().Parse(os.Args)
decryptCfg := rootCmd.Flags().Bool("decrypt-cfg", false, "unencrypt config file")
rootCmd.Flags().Parse(os.Args)
encryptCfg := !*decryptCfg
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(cmds.MakeListCmd(makeInitClient(cfg, initClient)))
rootCmd.AddCommand(cmds.MakeCreateCmd(masterpass, cfg, makeInitClient(cfg, initClient)))
rootCmd.AddCommand(cmds.MakeGetCmd(masterpass, cfg, makeInitClient(cfg, initClient)))
check(rootCmd.Execute())
}
func makeInitClient(cfg *viper.Viper, initClient types.CredentialClientInit) cmds.CredentialClientInit {
return func(ctx context.Context) types.CredentialClient {
connConfig := cfg.GetStringMapString(cmds.KeyConnConfig)
client, err := initClient(
ctx,
connConfig["target"],
connConfig["ca"],
connConfig["cert"],
connConfig["key"],
)
if err != nil {
fmt.Printf("Please run 'init' command before running API commands.\nError Message: %s\n", err)
os.Exit(1)
}
return client
}
}
func openConfig(cfgFile string) (masterpass string, v *viper.Viper) {
v = viper.New()
v.SetConfigType("toml")
if cfgFile != "" {
// Use config file from the flag.
v.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
check(err)
// Search config in home directory with name ".spc" (without extension).
v.AddConfigPath(home)
v.SetConfigName(".spc")
cfgFile = home + "/.spc.toml"
}
if _, err := os.Open(cfgFile); !os.IsNotExist(err) {
prompt := &survey.Password{Message: "Master password:"}
check(survey.AskOne(prompt, &masterpass, nil))
decryptConfig(masterpass, cfgFile)
}
//v.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := v.ReadInConfig(); err == nil {
fmt.Println("Using config file:", v.ConfigFileUsed())
}
return masterpass, v
}
func decryptConfig(masterpass string, cfgFile string) {
contents, err := ioutil.ReadFile(cfgFile)
check(err)
passkey, err := crypto.GenerateKeyFromPassword([]byte(masterpass))
check(err)
contents, err = crypto.CBCDecrypt(passkey, contents)
if err != nil && err.Error() == "Padding incorrect" {
fmt.Println("incorrect master password")
os.Exit(1)
} else if err != nil && err.Error() == "ciphertext is not a multiple of the block size" {
fmt.Println("Config wasn't encrypted.")
return
}
check(err)
check(ioutil.WriteFile(cfgFile, contents, 0600))
}
func encryptConfig(masterpass string, cfg *viper.Viper) {
contents, err := ioutil.ReadFile(cfg.ConfigFileUsed())
if os.IsNotExist(err) {
return
}
keypass, err := crypto.GenerateKeyFromPassword([]byte(masterpass))
check(err)
contents, err = crypto.CBCEncrypt(keypass, contents)
check(err)
check(ioutil.WriteFile(cfg.ConfigFileUsed(), contents, 0600))
}
func check(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

13
cmd/spc/main.go Normal file
View File

@ -0,0 +1,13 @@
package main
import (
"context"
"github.com/mitchell/selfpass/cmd/spc/cmd"
"github.com/mitchell/selfpass/credentials/repositories"
)
func main() {
ctx := context.Background()
cmd.Execute(ctx, repositories.NewCredentialServiceClient)
}

21
credentials/cmds/cmds.go Normal file
View File

@ -0,0 +1,21 @@
package cmds
import (
"context"
"fmt"
"os"
"github.com/mitchell/selfpass/credentials/types"
)
type CredentialClientInit func(ctx context.Context) (c types.CredentialClient)
func check(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
const KeyConnConfig = "connection"
const KeyPrivateKey = "private_key"

146
credentials/cmds/create.go Normal file
View File

@ -0,0 +1,146 @@
package cmds
import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"os"
"time"
"github.com/atotto/clipboard"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/credentials/types"
"github.com/mitchell/selfpass/crypto"
)
func MakeCreateCmd(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
createCmd := &cobra.Command{
Use: "create",
Short: "Create a credential in Selfpass",
Long: `Create a credential in Selfpass, and save it to the server after encrypting the
password.`,
Run: func(_ *cobra.Command, args []string) {
mdqs := []*survey.Question{
{
Name: "primary",
Prompt: &survey.Input{Message: "Primary user key:"},
},
{
Name: "sourceHost",
Prompt: &survey.Input{Message: "Source host:"},
},
{
Name: "loginURL",
Prompt: &survey.Input{Message: "Login url:"},
},
{
Name: "tag",
Prompt: &survey.Input{Message: "Tag:"},
},
}
cqs := []*survey.Question{
{
Name: "username",
Prompt: &survey.Input{Message: "Username:"},
},
{
Name: "email",
Prompt: &survey.Input{Message: "Email:"},
},
}
var ci types.CredentialInput
check(survey.Ask(mdqs, &ci.MetadataInput))
check(survey.Ask(cqs, &ci))
var newpass bool
prompt := &survey.Confirm{Message: "Do you want a random password?", Default: true}
check(survey.AskOne(prompt, &newpass, nil))
if newpass {
ci.Password = generatePassword(16, true, true)
var copypass bool
prompt = &survey.Confirm{Message: "Copy new pass to clipboard?", Default: true}
check(survey.AskOne(prompt, &copypass, nil))
if copypass {
check(clipboard.WriteAll(ci.Password))
}
} else {
prompt := &survey.Password{Message: "Password: "}
check(survey.AskOne(prompt, &ci.Password, nil))
var cpass string
prompt = &survey.Password{Message: "Confirm assword: "}
check(survey.AskOne(prompt, &cpass, nil))
if ci.Password != cpass {
fmt.Println("passwords didn't match'")
os.Exit(1)
}
}
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))
check(err)
ci.Password = base64.StdEncoding.EncodeToString(cipherpass)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
c, err := initClient(ctx).Create(ctx, ci)
check(err)
mdjson, err := json.MarshalIndent(c.Metadata, "", " ")
check(err)
fmt.Println(string(mdjson))
},
}
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)
}

63
credentials/cmds/get.go Normal file
View File

@ -0,0 +1,63 @@
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
}

58
credentials/cmds/list.go Normal file
View File

@ -0,0 +1,58 @@
package cmds
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/spf13/cobra"
)
func MakeListCmd(initClient CredentialClientInit) *cobra.Command {
var sourceHost string
listCmd := &cobra.Command{
Use: "list",
Short: "List the metadata for all credentials",
Long: `List the metadata for all credentials, with the option to filter by source host. Metadata
includes almost all the information but the most sensitive.`,
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
mdch, errch := initClient(ctx).GetAllMetadata(ctx, sourceHost)
receive:
for {
select {
case <-ctx.Done():
check(fmt.Errorf("context timeout"))
case err := <-errch:
check(err)
case md, ok := <-mdch:
if !ok {
break receive
}
mdjson, err := json.MarshalIndent(md, "", " ")
check(err)
fmt.Println(string(mdjson))
}
}
},
}
listCmd.Flags().StringVarP(
&sourceHost,
"source-host",
"s",
"",
"specify which source host to filter the results by",
)
return listCmd
}

View File

@ -266,6 +266,7 @@ type Metadata struct {
Primary string `protobuf:"bytes,4,opt,name=primary,proto3" json:"primary,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"`
Tag string `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -338,6 +339,13 @@ func (m *Metadata) GetLoginUrl() string {
return ""
}
func (m *Metadata) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
type Credential struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
CreatedAt *timestamp.Timestamp `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
@ -348,6 +356,7 @@ type Credential struct {
Password string `protobuf:"bytes,7,opt,name=password,proto3" json:"password,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"`
Tag string `protobuf:"bytes,10,opt,name=tag,proto3" json:"tag,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -441,6 +450,13 @@ func (m *Credential) GetLoginUrl() string {
return ""
}
func (m *Credential) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
type CredentialRequest struct {
Primary string `protobuf:"bytes,1,opt,name=primary,proto3" json:"primary,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
@ -448,6 +464,7 @@ type CredentialRequest struct {
Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,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"`
Tag string `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -520,6 +537,13 @@ func (m *CredentialRequest) GetLoginUrl() string {
return ""
}
func (m *CredentialRequest) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func init() {
proto.RegisterType((*DeleteResponse)(nil), "selfpass.credentials.DeleteResponse")
proto.RegisterType((*GetAllMetadataRequest)(nil), "selfpass.credentials.GetAllMetadataRequest")
@ -535,41 +559,41 @@ func init() {
func init() { proto.RegisterFile("credentials/protobuf/service.proto", fileDescriptor_ad34efc7bbd96e69) }
var fileDescriptor_ad34efc7bbd96e69 = []byte{
// 539 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0x95, 0x93, 0x34, 0x75, 0xa6, 0x21, 0x12, 0xab, 0x22, 0x59, 0xae, 0x44, 0xa2, 0x05, 0x89,
0xaa, 0x48, 0x2e, 0x2a, 0x17, 0x38, 0x86, 0x16, 0xa5, 0x1c, 0x00, 0xc9, 0xa5, 0x17, 0x2e, 0xd1,
0xd6, 0x9e, 0xa6, 0x96, 0x6c, 0xaf, 0xd9, 0x5d, 0x83, 0xfa, 0x01, 0xfc, 0x13, 0xfc, 0x11, 0x9f,
0x81, 0xbc, 0xf6, 0x26, 0xae, 0xeb, 0x34, 0xe1, 0xc6, 0xcd, 0xb3, 0x7e, 0x6f, 0xf5, 0xde, 0xcc,
0xce, 0x03, 0x1a, 0x08, 0x0c, 0x31, 0x55, 0x11, 0x8b, 0xe5, 0x71, 0x26, 0xb8, 0xe2, 0x57, 0xf9,
0xf5, 0xb1, 0x44, 0xf1, 0x3d, 0x0a, 0xd0, 0xd3, 0x07, 0x64, 0x5f, 0x62, 0x7c, 0x9d, 0x31, 0x29,
0xbd, 0x1a, 0xd8, 0x1d, 0x2f, 0x38, 0x5f, 0xc4, 0xb8, 0x22, 0xa9, 0x28, 0x41, 0xa9, 0x58, 0x92,
0x95, 0x34, 0x7a, 0x04, 0xa3, 0x33, 0x8c, 0x51, 0xa1, 0x8f, 0x32, 0xe3, 0xa9, 0x44, 0xe2, 0xc0,
0xae, 0xcc, 0x83, 0x00, 0xa5, 0x74, 0xac, 0x89, 0x75, 0x68, 0xfb, 0xa6, 0xa4, 0x6f, 0xe0, 0xc9,
0x0c, 0xd5, 0x34, 0x8e, 0x3f, 0xa2, 0x62, 0x21, 0x53, 0xcc, 0xc7, 0x6f, 0x39, 0x4a, 0x45, 0xc6,
0xb0, 0x27, 0x79, 0x2e, 0x02, 0x9c, 0xdf, 0x70, 0xa9, 0x34, 0x6d, 0xe0, 0x43, 0x79, 0x74, 0xce,
0xa5, 0xa2, 0x07, 0x30, 0xf8, 0x10, 0x1a, 0xf4, 0x08, 0x3a, 0x51, 0x58, 0x81, 0x3a, 0x51, 0x48,
0x6f, 0xe0, 0xd1, 0x65, 0x16, 0xb2, 0x42, 0x42, 0x2b, 0x80, 0xcc, 0x00, 0x56, 0x9e, 0x9c, 0xce,
0xc4, 0x3a, 0xdc, 0x3b, 0x79, 0xe1, 0xb5, 0xf9, 0xf5, 0x4e, 0x97, 0xdf, 0xd5, 0x65, 0x7e, 0x8d,
0x4a, 0x8f, 0x60, 0x78, 0x96, 0x27, 0xd9, 0xd2, 0xaa, 0x0b, 0x76, 0xc0, 0x53, 0x85, 0xa9, 0x2a,
0xbd, 0x0e, 0xfd, 0x65, 0x4d, 0x47, 0x30, 0x7c, 0x9f, 0x64, 0xea, 0xb6, 0xba, 0x87, 0xfe, 0xb1,
0xc0, 0x36, 0xbe, 0xef, 0x29, 0x7c, 0xab, 0x15, 0x32, 0x85, 0xe1, 0x9c, 0xa9, 0x4a, 0xa1, 0xeb,
0x95, 0xbd, 0xf7, 0x4c, 0xef, 0xbd, 0x2f, 0xa6, 0xf7, 0xfe, 0xa0, 0x42, 0x4f, 0x55, 0x41, 0xcd,
0xb5, 0x7b, 0x4d, 0xed, 0x6e, 0xa6, 0x56, 0xe8, 0xa9, 0x2a, 0x26, 0x95, 0x89, 0x28, 0x61, 0xe2,
0xd6, 0xe9, 0x69, 0x29, 0xa6, 0x6c, 0x0e, 0x64, 0xa7, 0x39, 0x10, 0x72, 0x00, 0x83, 0x98, 0x2f,
0xa2, 0x74, 0x9e, 0x8b, 0xd8, 0xe9, 0xeb, 0xdf, 0xb6, 0x3e, 0xb8, 0x14, 0x31, 0xfd, 0xd5, 0x01,
0x58, 0x35, 0xf2, 0xbf, 0x37, 0xeb, 0x82, 0x9d, 0x4b, 0x14, 0x29, 0x4b, 0xb0, 0x72, 0xba, 0xac,
0xc9, 0x3e, 0xec, 0x60, 0xc2, 0x22, 0xe3, 0xb1, 0x2c, 0x0a, 0x46, 0xf1, 0x72, 0x7e, 0x70, 0x11,
0x3a, 0xbb, 0x25, 0xc3, 0xd4, 0xcd, 0xd6, 0xd9, 0x0f, 0xb7, 0x6e, 0xd0, 0x68, 0xdd, 0x6f, 0x0b,
0x1e, 0xdf, 0x7b, 0x83, 0x75, 0xed, 0xd6, 0x7a, 0xed, 0x9d, 0x75, 0xda, 0xbb, 0xeb, 0xb4, 0xf7,
0x1e, 0xd6, 0xfe, 0x6f, 0x63, 0x3f, 0xf9, 0xd9, 0xab, 0x6b, 0xbf, 0x28, 0xd3, 0x85, 0xcc, 0x61,
0x74, 0x77, 0xe9, 0xc9, 0xcb, 0xf6, 0xd5, 0x6b, 0x8d, 0x06, 0xf7, 0x69, 0x3b, 0xd8, 0xc0, 0x5e,
0x59, 0xe4, 0x1c, 0xba, 0x33, 0x54, 0x64, 0xdc, 0x0e, 0x5c, 0xc6, 0x86, 0x3b, 0xd9, 0xb4, 0xf1,
0xe4, 0x02, 0xfa, 0xa7, 0xfa, 0xa9, 0x91, 0x6d, 0xd3, 0x61, 0x8b, 0x4b, 0x3f, 0x43, 0xbf, 0x4c,
0x27, 0xf2, 0xac, 0x1d, 0x7b, 0x27, 0xbb, 0xb6, 0xbb, 0xb0, 0x4c, 0xdc, 0xcd, 0x96, 0x9f, 0xb7,
0x03, 0x1a, 0x81, 0xfd, 0x09, 0x7a, 0x45, 0xaa, 0x11, 0xda, 0x8e, 0xae, 0xa7, 0x98, 0xbb, 0x06,
0x53, 0x4f, 0xc5, 0x77, 0xf0, 0xd5, 0x36, 0xab, 0x78, 0xd5, 0xd7, 0x5f, 0xaf, 0xff, 0x06, 0x00,
0x00, 0xff, 0xff, 0x39, 0xb2, 0x93, 0x8d, 0x82, 0x06, 0x00, 0x00,
// 535 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0xcf, 0x6e, 0xd3, 0x40,
0x10, 0xc6, 0xe5, 0xa4, 0x4d, 0xed, 0x69, 0x88, 0x60, 0x55, 0x24, 0xcb, 0x95, 0x48, 0x64, 0x90,
0xa8, 0x8a, 0xe4, 0xa2, 0x72, 0x81, 0x63, 0x68, 0x51, 0xca, 0x01, 0x21, 0xb9, 0xf4, 0xc2, 0x25,
0xda, 0xda, 0xd3, 0xd4, 0x92, 0xed, 0x35, 0xbb, 0x63, 0x50, 0x1f, 0x8c, 0x97, 0x80, 0x37, 0xe2,
0x84, 0xbc, 0xfe, 0x93, 0x3f, 0x75, 0x9a, 0x48, 0x5c, 0xb8, 0xed, 0xac, 0xbf, 0x59, 0xcd, 0xef,
0xdb, 0xf5, 0x07, 0x6e, 0x20, 0x31, 0xc4, 0x94, 0x22, 0x1e, 0xab, 0x93, 0x4c, 0x0a, 0x12, 0xd7,
0xf9, 0xcd, 0x89, 0x42, 0xf9, 0x3d, 0x0a, 0xd0, 0xd3, 0x1b, 0xec, 0x40, 0x61, 0x7c, 0x93, 0x71,
0xa5, 0xbc, 0x05, 0xb1, 0x33, 0x9c, 0x09, 0x31, 0x8b, 0x71, 0xde, 0x44, 0x51, 0x82, 0x8a, 0x78,
0x92, 0x95, 0x6d, 0xee, 0x31, 0x0c, 0xce, 0x31, 0x46, 0x42, 0x1f, 0x55, 0x26, 0x52, 0x85, 0xcc,
0x86, 0x3d, 0x95, 0x07, 0x01, 0x2a, 0x65, 0x1b, 0x23, 0xe3, 0xc8, 0xf4, 0xeb, 0xd2, 0x7d, 0x0b,
0x4f, 0x27, 0x48, 0xe3, 0x38, 0xfe, 0x84, 0xc4, 0x43, 0x4e, 0xdc, 0xc7, 0x6f, 0x39, 0x2a, 0x62,
0x43, 0xd8, 0x57, 0x22, 0x97, 0x01, 0x4e, 0x6f, 0x85, 0x22, 0xdd, 0x66, 0xf9, 0x50, 0x6e, 0x5d,
0x08, 0x45, 0xee, 0x21, 0x58, 0x1f, 0xc3, 0x5a, 0x3d, 0x80, 0x4e, 0x14, 0x56, 0xa2, 0x4e, 0x14,
0xba, 0xb7, 0xf0, 0xe8, 0x2a, 0x0b, 0x79, 0x31, 0x42, 0xab, 0x80, 0x4d, 0x00, 0xe6, 0x4c, 0x76,
0x67, 0x64, 0x1c, 0xed, 0x9f, 0xbe, 0xf4, 0xda, 0x78, 0xbd, 0xb3, 0x66, 0x5d, 0x1d, 0xe6, 0x2f,
0xb4, 0xba, 0xc7, 0xd0, 0x3f, 0xcf, 0x93, 0xac, 0x41, 0x75, 0xc0, 0x0c, 0x44, 0x4a, 0x98, 0x52,
0xc9, 0xda, 0xf7, 0x9b, 0xda, 0x1d, 0x40, 0xff, 0x43, 0x92, 0xd1, 0x5d, 0x75, 0x8e, 0xfb, 0xc7,
0x00, 0xb3, 0xe6, 0xbe, 0x37, 0xe1, 0x3b, 0x3d, 0x21, 0x27, 0x0c, 0xa7, 0x9c, 0xaa, 0x09, 0x1d,
0xaf, 0xf4, 0xde, 0xab, 0xbd, 0xf7, 0xbe, 0xd4, 0xde, 0xfb, 0x56, 0xa5, 0x1e, 0x53, 0xd1, 0x9a,
0x6b, 0x7a, 0xdd, 0xda, 0xdd, 0xdc, 0x5a, 0xa9, 0xc7, 0x54, 0xdc, 0x54, 0x26, 0xa3, 0x84, 0xcb,
0x3b, 0x7b, 0x47, 0x8f, 0x52, 0x97, 0xab, 0x17, 0xb2, 0xbb, 0x7a, 0x21, 0xec, 0x10, 0xac, 0x58,
0xcc, 0xa2, 0x74, 0x9a, 0xcb, 0xd8, 0xee, 0xe9, 0xcf, 0xa6, 0xde, 0xb8, 0x92, 0x31, 0x7b, 0x0c,
0x5d, 0xe2, 0x33, 0x7b, 0x4f, 0x6f, 0x17, 0x4b, 0xf7, 0x57, 0x07, 0x60, 0x6e, 0xed, 0x7f, 0x8f,
0xef, 0x80, 0x99, 0x2b, 0x94, 0x29, 0x4f, 0xb0, 0x62, 0x6f, 0x6a, 0x76, 0x00, 0xbb, 0x98, 0xf0,
0xa8, 0xa6, 0x2e, 0x8b, 0xa2, 0xa3, 0x78, 0x4b, 0x3f, 0x84, 0x0c, 0x2b, 0xee, 0xa6, 0x5e, 0x35,
0xd3, 0x7c, 0xd8, 0x4c, 0xab, 0xdd, 0x4c, 0x98, 0x9b, 0xf9, 0xdb, 0x80, 0x27, 0xf7, 0xde, 0xe9,
0x22, 0x8d, 0xb1, 0x9e, 0xa6, 0xb3, 0x8e, 0xa6, 0xbb, 0x8e, 0x66, 0xe7, 0x61, 0x9a, 0x7f, 0x7d,
0x1a, 0xa7, 0x3f, 0xbb, 0x8b, 0x34, 0x97, 0x65, 0x26, 0xb1, 0x29, 0x0c, 0x96, 0xa3, 0x82, 0xbd,
0x6a, 0xff, 0x61, 0x5b, 0x03, 0xc5, 0x79, 0xd6, 0x2e, 0xae, 0x65, 0xaf, 0x0d, 0x76, 0x01, 0xdd,
0x09, 0x12, 0x1b, 0xb6, 0x0b, 0x9b, 0xb0, 0x71, 0x46, 0x9b, 0x72, 0x82, 0x5d, 0x42, 0xef, 0x4c,
0x3f, 0x47, 0xb6, 0x6d, 0xa6, 0x6c, 0x71, 0xe8, 0x67, 0xe8, 0x95, 0x99, 0xc6, 0x9e, 0xb7, 0x6b,
0x97, 0x12, 0x6f, 0xbb, 0x03, 0xcb, 0x9c, 0xde, 0x8c, 0xfc, 0xa2, 0x5d, 0xb0, 0x1c, 0xf3, 0xef,
0xe1, 0xab, 0x59, 0xff, 0x5e, 0xd7, 0x3d, 0xbd, 0x7a, 0xf3, 0x37, 0x00, 0x00, 0xff, 0xff, 0x22,
0xb6, 0x48, 0x2d, 0x68, 0x06, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -589,7 +613,6 @@ type CredentialServiceClient interface {
Create(ctx context.Context, in *CredentialRequest, opts ...grpc.CallOption) (*Credential, error)
Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Credential, error)
Delete(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*DeleteResponse, error)
Dump(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*DumpResponse, error)
}
type credentialServiceClient struct {
@ -668,15 +691,6 @@ func (c *credentialServiceClient) Delete(ctx context.Context, in *IdRequest, opt
return out, nil
}
func (c *credentialServiceClient) Dump(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*DumpResponse, error) {
out := new(DumpResponse)
err := c.cc.Invoke(ctx, "/selfpass.credentials.CredentialService/Dump", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CredentialServiceServer is the server API for CredentialService service.
type CredentialServiceServer interface {
GetAllMetadata(*GetAllMetadataRequest, CredentialService_GetAllMetadataServer) error
@ -684,7 +698,6 @@ type CredentialServiceServer interface {
Create(context.Context, *CredentialRequest) (*Credential, error)
Update(context.Context, *UpdateRequest) (*Credential, error)
Delete(context.Context, *IdRequest) (*DeleteResponse, error)
Dump(context.Context, *EmptyRequest) (*DumpResponse, error)
}
// UnimplementedCredentialServiceServer can be embedded to have forward compatible implementations.
@ -706,9 +719,6 @@ func (*UnimplementedCredentialServiceServer) Update(ctx context.Context, req *Up
func (*UnimplementedCredentialServiceServer) Delete(ctx context.Context, req *IdRequest) (*DeleteResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
}
func (*UnimplementedCredentialServiceServer) Dump(ctx context.Context, req *EmptyRequest) (*DumpResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Dump not implemented")
}
func RegisterCredentialServiceServer(s *grpc.Server, srv CredentialServiceServer) {
s.RegisterService(&_CredentialService_serviceDesc, srv)
@ -807,24 +817,6 @@ func _CredentialService_Delete_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
func _CredentialService_Dump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).Dump(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/selfpass.credentials.CredentialService/Dump",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).Dump(ctx, req.(*EmptyRequest))
}
return interceptor(ctx, in, info, handler)
}
var _CredentialService_serviceDesc = grpc.ServiceDesc{
ServiceName: "selfpass.credentials.CredentialService",
HandlerType: (*CredentialServiceServer)(nil),
@ -845,10 +837,6 @@ var _CredentialService_serviceDesc = grpc.ServiceDesc{
MethodName: "Delete",
Handler: _CredentialService_Delete_Handler,
},
{
MethodName: "Dump",
Handler: _CredentialService_Dump_Handler,
},
},
Streams: []grpc.StreamDesc{
{

View File

@ -12,7 +12,7 @@ service CredentialService {
rpc Create (CredentialRequest) returns (Credential);
rpc Update (UpdateRequest) returns (Credential);
rpc Delete (IdRequest) returns (DeleteResponse);
rpc Dump (EmptyRequest) returns (DumpResponse);
// rpc Dump (EmptyRequest) returns (DumpResponse);
}
message DeleteResponse {
@ -46,6 +46,7 @@ message Metadata {
string primary = 4;
string source_host = 5;
string login_url = 6;
string tag = 7;
}
message Credential {
@ -58,6 +59,7 @@ message Credential {
string password = 7;
string source_host = 8;
string login_url = 9;
string tag = 10;
}
message CredentialRequest {
@ -67,4 +69,5 @@ message CredentialRequest {
string password = 4;
string source_host = 5;
string login_url = 6;
string tag = 7;
}

View File

@ -0,0 +1,115 @@
package repositories
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"github.com/mitchell/selfpass/credentials/endpoints"
"github.com/mitchell/selfpass/credentials/protobuf"
"github.com/mitchell/selfpass/credentials/transport"
"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) {
keypair, err := tls.X509KeyPair([]byte(cert), []byte(key))
if err != nil {
return nil, err
}
capool := x509.NewCertPool()
capool.AppendCertsFromPEM([]byte(ca))
creds := credentials.NewTLS(&tls.Config{
RootCAs: capool,
Certificates: []tls.Certificate{keypair},
})
conn, err := grpc.DialContext(ctx, target, grpc.WithTransportCredentials(creds), grpc.WithBlock())
if err != nil {
return nil, err
}
return CredentialServiceClient{
client: protobuf.NewCredentialServiceClient(conn),
}, nil
}
type CredentialServiceClient struct {
client protobuf.CredentialServiceClient
}
func (c CredentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
pbmdch := make(chan protobuf.Metadata, 1)
errch = make(chan error, 1)
stream, err := transport.DecodeMetdataStreamResponse(ctx, transport.ProtobufMetadataStream{
Metadata: pbmdch,
Errors: errch,
})
srv, err := c.client.GetAllMetadata(ctx, &protobuf.GetAllMetadataRequest{SourceHost: sourceHost})
if err != nil {
errch <- err
return nil, errch
}
go func() {
defer close(pbmdch)
for {
select {
case <-ctx.Done():
errch <- fmt.Errorf("context timeout")
return
default:
}
pbmd, err := srv.Recv()
if err == io.EOF {
return
} else if err != nil {
errch <- err
return
}
pbmdch <- *pbmd
}
}()
return stream.Metadata, stream.Errors
}
func (c CredentialServiceClient) Get(ctx context.Context, id string) (output types.Credential, err error) {
req := transport.EncodeIdRequest(endpoints.IDRequest{ID: id})
res, err := c.client.Get(ctx, &req)
if err != nil {
return output, err
}
return transport.DecodeCredential(*res)
}
func (c CredentialServiceClient) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
req := transport.EncodeCredentialRequest(ci)
res, err := c.client.Create(ctx, &req)
if err != nil {
return output, err
}
return transport.DecodeCredential(*res)
}
func (c CredentialServiceClient) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
panic("implement me")
}
func (c CredentialServiceClient) Delete(ctx context.Context, id string) (err error) {
panic("implement me")
}

View File

@ -7,18 +7,11 @@ import (
"github.com/mitchell/selfpass/credentials/types"
)
func NewRedisConn(cfg ConnConfig) (c RedisConn, err error) {
p, err := radix.NewPool(cfg.NetworkType, cfg.Address, int(cfg.Size), cfg.Options...)
func NewRedisConn(networkType, address string, connCount uint, options ...radix.PoolOpt) (c RedisConn, err error) {
p, err := radix.NewPool(networkType, address, int(connCount), options...)
return RedisConn{p: p}, err
}
type ConnConfig struct {
NetworkType string
Address string
Size uint
Options []radix.PoolOpt
}
type RedisConn struct {
p *radix.Pool
}
@ -30,22 +23,23 @@ func (conn RedisConn) GetAllMetadata(ctx context.Context, sourceHost string, err
defer close(mdch)
var key string
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: sourceHost + star})
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: types.TypePrefixCred + dash + sourceHost + star})
for scr.Next(&key) {
select {
case <-ctx.Done():
return
default:
var md types.Metadata
if err := conn.p.Do(radix.Cmd(&md, hGetAll, key)); err != nil {
errch <- err
return
}
mdch <- md
}
var md types.Metadata
if err := conn.p.Do(radix.Cmd(&md, hGetAll, key)); err != nil {
errch <- err
return
}
mdch <- md
}
}()
@ -53,41 +47,17 @@ func (conn RedisConn) GetAllMetadata(ctx context.Context, sourceHost string, err
}
func (conn RedisConn) Get(ctx context.Context, id string) (output types.Credential, err error) {
var key string
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: star + id, Count: 1})
if !scr.Next(&key) {
return output, nil
}
if err = scr.Close(); err != nil {
return output, err
}
err = conn.p.Do(radix.Cmd(&output, hGetAll, key))
err = conn.p.Do(radix.Cmd(&output, hGetAll, id))
return output, err
}
func (conn RedisConn) Put(ctx context.Context, c types.Credential) (err error) {
err = conn.p.Do(radix.FlatCmd(nil, hMSet, c.SourceHost+dash+c.ID, c))
err = conn.p.Do(radix.FlatCmd(nil, hMSet, c.ID, c))
return err
}
func (conn RedisConn) Delete(ctx context.Context, id string) (err error) {
var key string
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: star + id, Count: 1})
if !scr.Next(&key) {
return nil
}
if err = scr.Close(); err != nil {
return err
}
err = conn.p.Do(radix.Cmd(nil, del, key))
err = conn.p.Do(radix.Cmd(nil, del, id))
return err
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
"time"
"github.com/google/uuid"
"github.com/mitchell/selfpass/credentials/types"
)
@ -38,7 +37,7 @@ func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (ou
}
var c types.Credential
c.ID = "cred-" + uuid.New().String()
c.ID = generateID(ci)
c.CreatedAt = time.Now()
c.UpdatedAt = time.Now()
c.Primary = ci.Primary
@ -47,6 +46,7 @@ func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (ou
c.Username = ci.Username
c.Email = ci.Email
c.Password = ci.Password
c.Tag = ci.Tag
err = svc.repo.Put(ctx, c)
@ -57,6 +57,8 @@ func validateCredentialInput(c types.CredentialInput) (err error) {
switch {
case c.SourceHost == "":
return fmt.Errorf("%s must specify source host", types.InvalidArgument)
case c.Primary == "":
return fmt.Errorf("%s must specify primary user key", types.InvalidArgument)
case c.Password == "":
return fmt.Errorf("%s must specify password", types.InvalidArgument)
}
@ -64,6 +66,17 @@ func validateCredentialInput(c types.CredentialInput) (err error) {
return err
}
func generateID(ci types.CredentialInput) string {
idFormat := types.TypePrefixCred + "-%s-%s"
if ci.Tag != "" {
idFormat += "-%s"
return fmt.Sprintf(idFormat, ci.SourceHost, ci.Primary, ci.Tag)
}
return fmt.Sprintf(idFormat, ci.SourceHost, ci.Primary)
}
func (svc Credentials) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
if err = validateCredentialInput(ci); err != nil {
return output, err
@ -78,6 +91,7 @@ func (svc Credentials) Update(ctx context.Context, id string, ci types.Credentia
return output, err
}
c.ID = generateID(ci)
c.UpdatedAt = time.Now()
c.Primary = ci.Primary
c.LoginURL = ci.LoginURL
@ -85,6 +99,7 @@ func (svc Credentials) Update(ctx context.Context, id string, ci types.Credentia
c.Password = ci.Password
c.Email = ci.Email
c.Username = ci.Username
c.Tag = ci.Tag
return c, svc.repo.Put(ctx, c)
}

View File

@ -16,6 +16,13 @@ func decodeGetAllMetadataRequest(ctx context.Context, request interface{}) (inte
}, nil
}
func EncodeGetAllMetadataRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(endpoints.GetAllMetadataRequest)
return protobuf.GetAllMetadataRequest{
SourceHost: r.SourceHost,
}, nil
}
func encodeDumpResponse(ctx context.Context, response interface{}) (interface{}, error) {
r := response.(endpoints.DumpResponse)
return protobuf.DumpResponse{
@ -50,28 +57,69 @@ func encodeMetadataStreamResponse(ctx context.Context, response interface{}) (in
SourceHost: md.SourceHost,
Primary: md.Primary,
LoginUrl: md.LoginURL,
Tag: md.Tag,
}
}
}()
return protobufMetadataStream{
return ProtobufMetadataStream{
Metadata: pbmdch,
Errors: r.Errors,
}, nil
}
type protobufMetadataStream struct {
func DecodeMetdataStreamResponse(ctx context.Context, r ProtobufMetadataStream) (endpoints.MetadataStream, error) {
mdch := make(chan types.Metadata, 1)
errch := make(chan error, 1)
go func() {
defer close(mdch)
for pbmd := range r.Metadata {
createdAt, err := ptypes.Timestamp(pbmd.CreatedAt)
if err != nil {
errch <- err
return
}
updatedAt, err := ptypes.Timestamp(pbmd.UpdatedAt)
if err != nil {
errch <- err
return
}
mdch <- types.Metadata{
ID: pbmd.Id,
SourceHost: pbmd.SourceHost,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
Primary: pbmd.Primary,
LoginURL: pbmd.LoginUrl,
Tag: pbmd.Tag,
}
}
}()
return endpoints.MetadataStream{
Metadata: mdch,
Errors: errch,
}, nil
}
type ProtobufMetadataStream struct {
Metadata <-chan protobuf.Metadata
Errors chan error
}
func decodeCredentialRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(protobuf.CredentialRequest)
return types.CredentialInput{
MetadataInput: types.MetadataInput{
Primary: r.Primary,
LoginURL: r.LoginUrl,
SourceHost: r.SourceHost,
Tag: r.Tag,
},
Username: r.Username,
Email: r.Email,
@ -79,6 +127,18 @@ func decodeCredentialRequest(ctx context.Context, request interface{}) (interfac
}, nil
}
func EncodeCredentialRequest(r types.CredentialInput) protobuf.CredentialRequest {
return protobuf.CredentialRequest{
Primary: r.Primary,
Username: r.Username,
Email: r.Email,
Password: r.Password,
SourceHost: r.SourceHost,
LoginUrl: r.LoginURL,
Tag: r.Tag,
}
}
func encodeCredentialResponse(ctx context.Context, response interface{}) (interface{}, error) {
r := response.(types.Credential)
@ -99,14 +159,44 @@ func encodeCredentialResponse(ctx context.Context, response interface{}) (interf
Primary: r.Primary,
SourceHost: r.SourceHost,
LoginUrl: r.LoginURL,
Tag: r.Tag,
Username: r.Username,
Email: r.Email,
Password: r.Password,
}, nil
}
func DecodeCredential(r protobuf.Credential) (c types.Credential, err error) {
createdAt, err := ptypes.Timestamp(r.CreatedAt)
if err != nil {
return c, err
}
updatedAt, err := ptypes.Timestamp(r.UpdatedAt)
if err != nil {
return c, err
}
return types.Credential{
Metadata: types.Metadata{
ID: r.Id,
SourceHost: r.SourceHost,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
Primary: r.Primary,
LoginURL: r.LoginUrl,
Tag: r.Tag,
},
Username: r.Username,
Email: r.Email,
Password: r.Password,
}, nil
}
func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(protobuf.UpdateRequest)
return endpoints.UpdateRequest{
ID: r.Id,
Credential: types.CredentialInput{
@ -114,6 +204,7 @@ func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
Primary: r.Credential.Primary,
SourceHost: r.Credential.SourceHost,
LoginURL: r.Credential.LoginUrl,
Tag: r.Credential.Tag,
},
Username: r.Credential.Username,
Email: r.Credential.Email,
@ -122,6 +213,24 @@ func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
}, nil
}
func EncodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(endpoints.UpdateRequest)
c := r.Credential
return protobuf.UpdateRequest{
Id: r.ID,
Credential: &protobuf.CredentialRequest{
Primary: c.Primary,
Username: c.Username,
Email: c.Email,
Password: c.Password,
SourceHost: c.SourceHost,
LoginUrl: c.LoginURL,
Tag: c.Tag,
},
}, nil
}
func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(protobuf.IdRequest)
return endpoints.IDRequest{
@ -129,6 +238,12 @@ func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, err
}, nil
}
func noOp(ctx context.Context, request interface{}) (interface{}, error) {
func EncodeIdRequest(r endpoints.IDRequest) protobuf.IdRequest {
return protobuf.IdRequest{
Id: r.ID,
}
}
func noOp(context.Context, interface{}) (interface{}, error) {
return nil, nil
}

View File

@ -75,7 +75,7 @@ func (s GRPCServer) GetAllMetadata(r *protobuf.GetAllMetadataRequest, srv protob
return err
}
mds := i.(protobufMetadataStream)
mds := i.(ProtobufMetadataStream)
receiveLoop:
for {

View File

@ -4,11 +4,13 @@ import (
"time"
)
const TypePrefixCred = "cred"
type Credential struct {
Metadata
Username string
Email string
Password string
Password string `json:"-"`
}
type CredentialInput struct {
@ -25,10 +27,12 @@ type Metadata struct {
UpdatedAt time.Time
Primary string
LoginURL string
Tag string
}
type MetadataInput struct {
Primary string
SourceHost string
LoginURL string
Tag string
}

View File

@ -18,3 +18,13 @@ type CredentialRepo interface {
Delete(ctx context.Context, id string) (err error)
DumpDB(ctx context.Context) (bs []byte, err error)
}
type CredentialClientInit func(ctx context.Context, target, ca, cert, key string) (c CredentialClient, err error)
type CredentialClient interface {
GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan Metadata, errch chan error)
Get(ctx context.Context, id string) (output Credential, err error)
Create(ctx context.Context, ci CredentialInput) (output Credential, err error)
Update(ctx context.Context, id string, ci CredentialInput) (output Credential, err error)
Delete(ctx context.Context, id string) (err error)
}

99
crypto/cbc.go Normal file
View File

@ -0,0 +1,99 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
"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) {
if len(key) != 32 {
return nil, fmt.Errorf("key is not 32 bytes")
}
plaintext = padding.AddPadding(plaintext)
if len(plaintext)%aes.BlockSize != 0 {
return nil, fmt.Errorf("plaintext is not a multiple of the block size")
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
return ciphertext, nil
}
func CBCDecrypt(key []byte, ciphertext []byte) ([]byte, error) {
if len(key) != 32 {
return nil, fmt.Errorf("key is not 32 bytes")
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(ciphertext) < aes.BlockSize {
return nil, err
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
if len(ciphertext)%aes.BlockSize != 0 {
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
return padding.RemovePadding(ciphertext)
}

21
docker-compose.yml Normal file
View File

@ -0,0 +1,21 @@
version: "3.7"
services:
redis:
image: "redis:alpine"
selfpass:
build: .
entrypoint:
- server
ports:
- "8080:8080"
secrets:
- ca_cert
- server_cert
- server_key
secrets:
ca_cert:
file: ./certs/ca.pem
server_cert:
file: ./certs/server.pem
server_key:
file: ./certs/server-key.pem

View File

@ -1,36 +0,0 @@
#!/bin/bash
# Start the first process
redis-server ./redis.conf &
status=$?
if [ $status -ne 0 ]; then
echo "Failed to start my_first_process: $status"
exit $status
fi
# Start the second process
./server --dev -v &
status=$?
if [ $status -ne 0 ]; then
echo "Failed to start my_second_process: $status"
exit $status
fi
# Naive check runs checks once a minute to see if either of the processes exited.
# This illustrates part of the heavy lifting you need to do if you want to run
# more than one service in a container. The container exits with an error
# if it detects that either of the processes has exited.
# Otherwise it loops forever, waking up every 60 seconds
while sleep 60; do
ps aux |grep my_first_process |grep -q -v grep
PROCESS_1_STATUS=$?
ps aux |grep my_second_process |grep -q -v grep
PROCESS_2_STATUS=$?
# If the greps above find anything, they exit with 0 status
# If they are not both 0, then something is wrong
if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
echo "One of the processes has already exited."
exit 1
fi
done

24
go.mod
View File

@ -1,15 +1,33 @@
module github.com/mitchell/selfpass
require (
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b // indirect
github.com/atotto/clipboard v0.1.2
github.com/aws/aws-sdk-go-v2 v0.7.0
github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692
github.com/go-kit/kit v0.8.0
github.com/go-logfmt/logfmt v0.4.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/protobuf v1.3.1
github.com/google/uuid v1.1.1
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kr/pty v1.1.4 // indirect
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/mediocregopher/radix/v3 v3.2.3
github.com/mitchellh/go-homedir v1.1.0
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.3.2
github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
golang.org/x/sys v0.0.0-20190416152802-12500544f89f // indirect
google.golang.org/grpc v1.20.0
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect
golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/appengine v1.4.0 // indirect
google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52 // indirect
google.golang.org/grpc v1.20.1
gopkg.in/AlecAivazis/survey.v1 v1.8.4
)

108
go.sum
View File

@ -1,12 +1,26 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b h1:sSQK05nvxs4UkgCJaxihteu+r+6ela3dNMm7NVmsS3c=
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=
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/go.mod h1:17MaCZ9g0q5BIMxwzRQeiv8M3c8+W7iuBnlWAEprcxE=
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/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
@ -14,8 +28,6 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -25,45 +37,123 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gucumber/gucumber v0.0.0-20180127021336-7d5c79e832a2/go.mod h1:YbdHRK9ViqwGMS0rtRY+1I6faHvVyyurKPIPwifihxI=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c h1:kp3AxgXgDOmIJFR7bIwqFhwJ2qWar8tEQSE5XXhCfVk=
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed h1:3dQJqqDouawQgl3gBE1PNHKFkJYGEuFb1DbSlaxdosE=
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
github.com/mediocregopher/radix/v3 v3.2.3 h1:TbcGCZdo9zfPYPgevsqRn+OjvCyfOK6TzuXhqzWdCt0=
github.com/mediocregopher/radix/v3 v3.2.3/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI=
golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190416152802-12500544f89f h1:1ZH9RnjNgLzh6YrsRp/c6ddZ8Lq0fq9xztNOoWJ2sz4=
golang.org/x/sys v0.0.0-20190416152802-12500544f89f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb h1:k07iPOt0d6nEnwXF+kHB+iEg+WSuKe/SOQuFM2QoD+E=
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.20.0 h1:DlsSIrgEBuZAUFJcta2B5i/lzeHHbnfkNFAfFXLVFYQ=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52 h1:LHc/6x2dMeCKkSsrVgo4DY+Z566T1OeoMwLtdfoy8LE=
google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
gopkg.in/AlecAivazis/survey.v1 v1.8.4/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -3,4 +3,4 @@ grpcurl -cacert ./certs/ca.pem \
-key ./certs/client-key.pem \
-proto ./credentials/protobuf/service.proto \
localhost:8080 \
selfpass.credentials.CredentialService/Dump
selfpass.credentials.CredentialService/GetAllMetadata

1317
redis.conf

File diff suppressed because it is too large Load Diff