Move service related filed to services folder

This commit is contained in:
mitchell 2019-07-10 22:33:22 -04:00
parent 347fbe7268
commit 7d770ef150
41 changed files with 50 additions and 50 deletions

13
services/Dockerfile Normal file
View file

@ -0,0 +1,13 @@
FROM golang:1.11.5 as build
WORKDIR /go/src/github.com/mitchell/selfpass/services
COPY . .
ENV GO111MODULE on
RUN make build
FROM debian:stable-20190506-slim
COPY --from=build /go/src/github.com/mitchell/selfpass/services/bin/server /usr/bin/server
RUN groupadd -r selfpass && useradd --no-log-init -r -g selfpass selfpass
USER selfpass
WORKDIR /home/selfpass
ENTRYPOINT ["server"]
EXPOSE 8080

94
services/Makefile Normal file
View file

@ -0,0 +1,94 @@
.PHONY: all build clean format test gen-certs-go
docker: clean format install
docker-compose build
build: gen-certs-go
go build -mod=vendor -o ./bin/server ./cmd/server
rm ./cmd/server/certs.go
clean:
rm -rf ./bin ./vendor ./cmd/server/certs.go
local:
docker-compose up -d
up:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
upd:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
down:
docker-compose down
machine-create-google:
docker-machine create --driver google \
--google-address selfpass \
--google-project selfpass-241808 \
--google-machine-type g1-small \
--google-machine-image https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-9-stretch-v20190514 \
--google-username selfpass \
--google-zone us-west1-c \
selfpass01
$(MAKE) machine-put-redis.conf
$(MAKE) machine-put-data
$(MAKE) machine-install-stackdriver-agent
$(MAKE) machine-add-grpc-server-tag
machine-rm:
docker-machine rm selfpass01
machine-ssh:
docker-machine ssh selfpass01
machine-put-redis.conf:
docker-machine scp ./redis.conf selfpass01:redis.conf
machine-put-data:
docker-machine scp -r ./data selfpass01:
machine-get-data:
docker-machine scp -r selfpass01:data ./
machine-add-grpc-server-tag:
gcloud compute instances add-tags selfpass01 \
--zone us-west1-c \
--tags grpc-server
machine-install-stackdriver-agent:
docker-machine ssh selfpass01 "curl -sSO https://dl.google.com/cloudagents/install-monitoring-agent.sh && sudo bash install-monitoring-agent.sh"
format:
gofmt -w -s -l .
install:
go mod tidy
go mod vendor
install-spc:
go install ./cmd/spc
gen-protoc:
protoc --go_out=plugins=grpc:. \
--dart_out=grpc:. \
./credentials/protobuf/service.proto
gen-csr-json:
mkdir certs
cd certs && cfssl print-defaults csr > csr.json
gen-ca:
cd certs && cfssl genkey -initca csr.json | cfssljson -bare ca
gen-server-cert:
cd certs && cfssl gencert -ca ca.pem -ca-key ca-key.pem csr.json | cfssljson -bare server
gen-client-cert:
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:
go test -cover ./...

52
services/README.md Normal file
View file

@ -0,0 +1,52 @@
# selfpass
This is the project home of *selfpass*, the self-hosted password manager. This project is intended
to be a single-user (or **trusted** multi-user) password manager capable of encrypting/decrypting
credentials and storing them remotely through encrypted transportation, all of which is deployable
locally or to popular cloud platforms such as GCP and AWS.
It is still currently in development. However, the service is already capable of serving a gRPC based
API using mutual TLS encryption, backed by Redis and Docker. It is also capable of being deployed in
a semi-automated fashion locally and to GCP thanks to Docker.
In addition to the service there is `spc` (**s**elf**p**ass **C**LI), which is a fully fledged *selfpass* client
capable of interacting with the whole selfpass API and creating AES-GCM encrypted credentials using
a *private key* and *master password*. All of which is done using mutual TLS and an AES-GCM
encrypted config.
#### Service Roadmap
| Goal | Progress | Comment |
| --- | :---: | --- |
| Support credentials CRUD on gRPC API. | 100% | |
| Enable server-side mutual TLS, using cfssl. | 100% | |
| Deployable on Docker. | 100% | |
| Automatically deployable to GCP using docker-machine and Terraform. | 50% | TODO: Terraform |
| Automatically deployable to AWS using docker-machine and Terraform. | 0% | |
#### SPC Roadmap
| Goal | Progress | Comment |
| --- | :---: | --- |
| Support credentials CRUD via gRPC. | 100% | |
| Support mutual TLS. | 100% | |
| Support storage of certs, PK, and host in AES-CBC encrypted config. | 100% | |
| Support AES-GCM encryption of passes and OTP secrets, using MP and PK. | 100% | |
| Support AES-GCM encryption of local files, using MP and PK. | 100% | |
#### Unplanned Goals
- Web client.
- Sensitive financial info support.
- Miscellaneous text/file encryption and storage support.
- Vault separation.
#### 3rd-party Technologies in Use (and where):
- Golang (all)
- Go-Kit (all)
- gRPC (all)
- Cobra Commander & Viper Config (spc)
- Redis (service)
- Docker (service)
- Debian (docker images and machines)

View file

@ -0,0 +1,50 @@
package commands
import (
"fmt"
"io/ioutil"
"os"
"github.com/spf13/cobra"
"github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/crypto"
)
func makeDecrypt(repo types.ConfigRepo) *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) {
masterpass, cfg, err := repo.OpenConfig()
check(err)
file := args[0]
fileout := file
if file[len(file)-4:] == ".enc" {
fileout = file[:len(file)-4]
}
contents, err := ioutil.ReadFile(file)
check(err)
key := cfg.GetString(types.KeyPrivateKey)
passkey := crypto.GeneratePBKDF2Key([]byte(masterpass), []byte(key))
contents, err = crypto.GCMDecrypt(passkey, contents)
check(err)
check(ioutil.WriteFile(fileout, contents, 0600))
check(os.Remove(file))
fmt.Println("Decrypted file: ", fileout)
},
}
return decryptCmd
}

View file

@ -0,0 +1,28 @@
package commands
import (
"fmt"
"github.com/spf13/cobra"
"github.com/mitchell/selfpass/services/cli/types"
)
func makeDecryptCfg(repo types.ConfigRepo) *cobra.Command {
decryptCfg := &cobra.Command{
Use: "decrypt-cfg",
Short: "Decrypt your config file",
Long: "Decrypt your config file, so you can access your private key, host, and certs.",
Run: func(cmd *cobra.Command, args []string) {
_, _, err := repo.OpenConfig()
check(err)
check(repo.DecryptConfig())
fmt.Println("Config decrypted. It will automatically encrypt next run of spc.")
},
}
return decryptCfg
}

View file

@ -0,0 +1,46 @@
package commands
import (
"fmt"
"io/ioutil"
"os"
"github.com/spf13/cobra"
"github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/crypto"
)
func makeEncrypt(repo types.ConfigRepo) *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) {
masterpass, cfg, err := repo.OpenConfig()
check(err)
file := args[0]
fileEnc := file + ".enc"
contents, err := ioutil.ReadFile(file)
check(err)
key := cfg.GetString(types.KeyPrivateKey)
passkey := crypto.GeneratePBKDF2Key([]byte(masterpass), []byte(key))
contents, err = crypto.GCMEncrypt(passkey, contents)
check(err)
check(ioutil.WriteFile(fileEnc, contents, 0600))
check(os.Remove(file))
fmt.Println("Encrypted file: ", fileEnc)
},
}
return encryptCmd
}

View file

@ -0,0 +1,87 @@
package commands
import (
"fmt"
"io/ioutil"
"strings"
"github.com/google/uuid"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/services/cli/types"
)
func makeInit(repo types.ConfigRepo) *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)
)
_, cfg, _ := repo.OpenConfig()
prompt = &survey.Password{Message: "New 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"))
}
repo.SetMasterpass(masterpass)
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.Password{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(types.KeyConnConfig, map[string]string{
"target": target,
"ca": string(ca),
"cert": string(cert),
"key": string(key),
})
cfg.Set(types.KeyPrivateKey, privateKey)
check(repo.WriteConfig())
},
}
return initCmd
}

View file

@ -0,0 +1,72 @@
package commands
import (
"context"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/mitchell/selfpass/services/cli/repositories"
"github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/credentials/commands"
credrepos "github.com/mitchell/selfpass/services/credentials/repositories"
credtypes "github.com/mitchell/selfpass/services/credentials/types"
)
func Execute() {
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.`,
Version: "v0.1.0",
}
cfgFile := rootCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.spc.toml)")
mgr := repositories.NewConfigManager(cfgFile)
clientInit := credrepos.NewCredentialServiceClient
rootCmd.AddCommand(
makeInit(mgr),
makeEncrypt(mgr),
makeDecrypt(mgr),
makeDecryptCfg(mgr),
commands.MakeList(makeInitClient(mgr, clientInit)),
commands.MakeCreate(mgr, makeInitClient(mgr, clientInit)),
commands.MakeUpdate(mgr, makeInitClient(mgr, clientInit)),
commands.MakeGet(mgr, makeInitClient(mgr, clientInit)),
commands.MakeDelete(makeInitClient(mgr, clientInit)),
commands.MakeGCMToCBC(mgr, makeInitClient(mgr, clientInit)),
)
check(rootCmd.Execute())
}
func makeInitClient(repo types.ConfigRepo, initClient credtypes.CredentialClientInit) commands.CredentialClientInit {
return func(ctx context.Context) credtypes.CredentialClient {
_, cfg, err := repo.OpenConfig()
check(err)
connConfig := cfg.GetStringMapString(types.KeyConnConfig)
client, err := initClient(
ctx,
connConfig["target"],
connConfig["ca"],
connConfig["cert"],
connConfig["key"],
)
check(err)
return client
}
}
func check(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View file

@ -0,0 +1,159 @@
package repositories
import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/crypto"
)
func NewConfigManager(cfgFile *string) *ConfigManager {
return &ConfigManager{
cfgFile: cfgFile,
}
}
type ConfigManager struct {
masterpass string
cfgFile *string
v *viper.Viper
}
func (mgr *ConfigManager) SetMasterpass(masterpass string) {
mgr.masterpass = masterpass
}
func (mgr *ConfigManager) OpenConfig() (output string, v *viper.Viper, err error) {
if mgr.masterpass != "" {
return mgr.masterpass, mgr.v, nil
}
cfg := *mgr.cfgFile
mgr.v = viper.New()
mgr.v.SetConfigType("toml")
if cfg == "" {
home, err := homedir.Dir()
if err != nil {
return output, nil, err
}
cfg = home + "/.spc.toml"
}
mgr.v.SetConfigFile(cfg)
mgr.cfgFile = &cfg
var contents []byte
var configDecrypted bool
if _, err := os.Open(cfg); os.IsNotExist(err) {
return output, mgr.v, fmt.Errorf("no config found, run 'init' command")
}
prompt := &survey.Password{Message: "Master password:"}
if err = survey.AskOne(prompt, &mgr.masterpass, nil); err != nil {
return output, nil, err
}
contents, err = decryptConfig(mgr.masterpass, cfg)
if err != nil && err == errConfigDecrypted {
configDecrypted = true
} else if err != nil {
return output, nil, err
}
if err = mgr.v.ReadConfig(bytes.NewBuffer(contents)); err != nil && strings.HasPrefix(err.Error(), "While parsing config") {
return output, nil, fmt.Errorf("incorrect master password")
} else if err != nil {
return output, nil, err
}
if configDecrypted {
fmt.Println("Config wasn't encrypted, or has been compromised.")
if err = mgr.WriteConfig(); err != nil {
return output, nil, err
}
}
return mgr.masterpass, mgr.v, nil
}
var errConfigDecrypted = errors.New("config is decrypted")
func decryptConfig(masterpass string, cfgFile string) (contents []byte, err error) {
contents, err = ioutil.ReadFile(cfgFile)
if err != nil {
return contents, err
}
if string(contents[:len(types.KeyPrivateKey)]) == types.KeyPrivateKey {
return contents, errConfigDecrypted
}
salt := contents[:saltSize]
contents = contents[saltSize:]
passkey := crypto.GeneratePBKDF2Key([]byte(masterpass), salt)
plaintext, err := crypto.GCMDecrypt(passkey, contents)
if err != nil {
return contents, err
}
return plaintext, nil
}
func (mgr ConfigManager) DecryptConfig() error {
if err := mgr.v.WriteConfig(); err != nil {
return err
}
return nil
}
func (mgr ConfigManager) WriteConfig() (err error) {
if err := mgr.v.WriteConfigAs(*mgr.cfgFile); err != nil {
return err
}
contents, err := ioutil.ReadFile(mgr.v.ConfigFileUsed())
if err != nil {
return err
}
salt := make([]byte, saltSize)
_, err = rand.Read(salt)
if err != nil {
return err
}
keypass := crypto.GeneratePBKDF2Key([]byte(mgr.masterpass), salt)
contents, err = crypto.GCMEncrypt(keypass, contents)
if err != nil {
return err
}
contents = append(salt, contents...)
err = ioutil.WriteFile(mgr.v.ConfigFileUsed(), contents, 0600)
if err != nil {
return err
}
return nil
}
const saltSize = 16

View file

@ -0,0 +1,13 @@
package types
import "github.com/spf13/viper"
type ConfigRepo interface {
OpenConfig() (masterpass string, cfg *viper.Viper, err error)
DecryptConfig() (err error)
SetMasterpass(masterpass string)
WriteConfig() (err error)
}
const KeyPrivateKey = "private_key"
const KeyConnConfig = "connection"

View file

@ -0,0 +1,113 @@
package main
import (
"crypto/tls"
"crypto/x509"
"flag"
"io"
stdlog "log"
"net"
"os"
"os/signal"
"syscall"
"github.com/go-kit/kit/log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"github.com/mitchell/selfpass/services/credentials/middleware"
"github.com/mitchell/selfpass/services/credentials/protobuf"
"github.com/mitchell/selfpass/services/credentials/repositories"
"github.com/mitchell/selfpass/services/credentials/service"
"github.com/mitchell/selfpass/services/credentials/transport"
"github.com/mitchell/selfpass/services/credentials/types"
)
var logger log.Logger
func main() {
var (
stop = make(chan os.Signal, 1)
jsonLogs = flag.Bool("json-logs", false, "enables json logging")
port = flag.String("port", "8080", "specify the port to listen on")
verbose = flag.Bool("v", false, "be more verbose")
)
flag.Parse()
signal.Notify(stop, syscall.SIGINT)
signal.Notify(stop, syscall.SIGKILL)
signal.Notify(stop, syscall.SIGTERM)
logger = newLogger(os.Stdout, *jsonLogs)
keypair, err := tls.X509KeyPair([]byte(cert), []byte(key))
check(err)
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM([]byte(ca))
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{keypair},
ClientCAs: caPool,
ClientAuth: tls.RequireAndVerifyClientCert,
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
CurvePreferences: []tls.CurveID{
tls.CurveP256,
},
})
db, err := repositories.NewRedisConn("tcp", "redis:6379", 2)
check(err)
var svc types.Service
svc = service.NewCredentials(db)
if *verbose {
svc = middleware.NewServiceLogger(logger, svc)
}
gsrv := transport.NewGRPCServer(svc, logger)
srv := grpc.NewServer(grpc.Creds(creds))
protobuf.RegisterCredentialServiceServer(srv, gsrv)
addr := ":" + *port
lis, err := net.Listen("tcp", addr)
check(err)
_ = logger.Log(
"message", "serving",
"address", addr,
)
go func() { check(srv.Serve(lis)) }()
<-stop
_ = logger.Log("message", "gracefully stopping")
srv.GracefulStop()
}
func newLogger(writer io.Writer, jsonLogs bool) log.Logger {
writer = log.NewSyncWriter(writer)
l := log.NewLogfmtLogger(writer)
if jsonLogs {
l = log.NewJSONLogger(writer)
}
l = log.WithPrefix(l, "caller", log.Caller(5), "timestamp", log.DefaultTimestamp)
lfunc := log.LoggerFunc(func(in ...interface{}) error {
if err := l.Log(in...); err != nil {
stdlog.Println(err)
}
return nil
})
return lfunc
}
func check(err error) {
if err != nil {
_ = logger.Log("error", err)
os.Exit(1)
}
}

9
services/cmd/spc/main.go Normal file
View file

@ -0,0 +1,9 @@
package main
import (
"github.com/mitchell/selfpass/services/cli/commands"
)
func main() {
commands.Execute()
}

View file

@ -0,0 +1,100 @@
package commands
import (
"context"
"fmt"
"os"
"sort"
"time"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/services/credentials/types"
)
type CredentialClientInit func(ctx context.Context) (c types.CredentialClient)
func check(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func selectCredential(client types.CredentialClient) types.Credential {
var (
idKey string
source string
prompt survey.Prompt
)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
mdch, errch := client.GetAllMetadata(ctx, "")
mds := map[string][]types.Metadata{}
fmt.Println()
receive:
for {
select {
case <-ctx.Done():
check(ctx.Err())
case err := <-errch:
check(err)
case md, ok := <-mdch:
if !ok {
break receive
}
mds[md.SourceHost] = append(mds[md.SourceHost], md)
}
}
sources := []string{}
for source := range mds {
sources = append(sources, source)
}
sort.Strings(sources)
prompt = &survey.Select{
Message: "Source host:",
Options: sources,
PageSize: 20,
VimMode: true,
}
check(survey.AskOne(prompt, &source, nil))
keys := []string{}
keyIDMap := map[string]string{}
for _, md := range mds[source] {
key := md.Primary
if md.Tag != "" {
key += "-" + md.Tag
}
keys = append(keys, key)
keyIDMap[key] = md.ID
}
prompt = &survey.Select{
Message: "Primary user key (and tag):",
Options: keys,
PageSize: 20,
VimMode: true,
}
check(survey.AskOne(prompt, &idKey, nil))
ctx, cancel = context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
cred, err := client.Get(ctx, keyIDMap[idKey])
check(err)
return cred
}

View file

@ -0,0 +1,155 @@
package commands
import (
"context"
"encoding/base64"
"fmt"
"os"
"time"
"github.com/atotto/clipboard"
"github.com/pquerna/otp/totp"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
clitypes "github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/credentials/types"
"github.com/mitchell/selfpass/services/crypto"
)
func MakeCreate(repo clitypes.ConfigRepo, initClient CredentialClientInit) *cobra.Command {
var length uint
var numbers bool
var specials bool
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) {
var (
otp bool
cleancb bool
newpass bool
ci types.CredentialInput
)
masterpass, cfg, err := repo.OpenConfig()
check(err)
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:"},
},
}
check(survey.Ask(mdqs, &ci.MetadataInput))
check(survey.Ask(cqs, &ci))
key := cfg.GetString(clitypes.KeyPrivateKey)
keypass := crypto.GeneratePBKDF2Key([]byte(masterpass), []byte(key))
prompt := &survey.Confirm{Message: "Do you want a random password?", Default: true}
check(survey.AskOne(prompt, &newpass, nil))
if newpass {
ci.Password = crypto.GeneratePassword(int(length), numbers, specials)
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 password: "}
check(survey.AskOne(prompt, &cpass, nil))
if ci.Password != cpass {
fmt.Println("passwords didn't match")
os.Exit(1)
}
}
cipherpass, err := crypto.CBCEncrypt(keypass, []byte(ci.Password))
check(err)
ci.Password = base64.StdEncoding.EncodeToString(cipherpass)
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, &copyotp, nil))
if copyotp {
otp, err := totp.GenerateCode(secret, time.Now())
check(err)
check(clipboard.WriteAll(otp))
}
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*25)
defer cancel()
c, err := initClient(ctx).Create(ctx, ci)
check(err)
fmt.Println(c)
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
}

View file

@ -0,0 +1,42 @@
package commands
import (
"context"
"fmt"
"time"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
)
func MakeDelete(initClient CredentialClientInit) *cobra.Command {
deleteCmd := &cobra.Command{
Use: "delete",
Short: "Delete a credential using the given ID",
Long: `Delete a credential using the given ID, permanently. THERE IS NO UNDOING THIS ACTION.`,
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
client := initClient(ctx)
cred := selectCredential(client)
fmt.Println(cred)
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*25)
defer cancel()
check(initClient(ctx).Delete(ctx, cred.ID))
}
},
}
return deleteCmd
}

View file

@ -0,0 +1,108 @@
package commands
import (
"context"
"encoding/base64"
"encoding/hex"
"fmt"
"time"
clitypes "github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/credentials/types"
"github.com/mitchell/selfpass/services/crypto"
"github.com/spf13/cobra"
)
func MakeGCMToCBC(repo clitypes.ConfigRepo, initClient CredentialClientInit) *cobra.Command {
gcmToCBC := &cobra.Command{
Use: "gcm-to-cbc",
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
masterpass, cfg, err := repo.OpenConfig()
check(err)
privKey := cfg.GetString(clitypes.KeyPrivateKey)
fmt.Println(privKey)
oldHex, err := hex.DecodeString(privKey)
check(err)
oldKey, err := crypto.CombinePasswordAndKey([]byte(masterpass), oldHex)
check(err)
key := crypto.GeneratePBKDF2Key([]byte(masterpass), []byte(privKey))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
client := initClient(ctx)
mdch, errch := client.GetAllMetadata(ctx, "")
receive:
for {
select {
case <-ctx.Done():
check(ctx.Err())
case err := <-errch:
check(err)
case md, ok := <-mdch:
if !ok {
break receive
}
cred, err := client.Get(ctx, md.ID)
check(err)
cipherpass, err := base64.StdEncoding.DecodeString(cred.Password)
check(err)
plainpass, err := crypto.GCMDecrypt(oldKey, cipherpass)
check(err)
cipherpass, err = crypto.CBCEncrypt(key, plainpass)
check(err)
password := base64.StdEncoding.EncodeToString(cipherpass)
var otpSecret string
if cred.OTPSecret != "" {
ciphersecret, err := base64.StdEncoding.DecodeString(cred.OTPSecret)
check(err)
plainsecret, err := crypto.GCMDecrypt(oldKey, ciphersecret)
check(err)
ciphersecret, err = crypto.CBCEncrypt(key, plainsecret)
check(err)
otpSecret = base64.StdEncoding.EncodeToString(ciphersecret)
}
credIn := types.CredentialInput{
MetadataInput: types.MetadataInput{
Primary: cred.Primary,
SourceHost: cred.SourceHost,
LoginURL: cred.LoginURL,
Tag: cred.Tag,
},
Username: cred.Username,
Email: cred.Email,
Password: password,
OTPSecret: otpSecret,
}
_, err = client.Update(ctx, cred.ID, credIn)
check(err)
}
}
},
}
return gcmToCBC
}

View file

@ -0,0 +1,94 @@
package commands
import (
"context"
"encoding/base64"
"fmt"
"time"
"github.com/atotto/clipboard"
"github.com/pquerna/otp/totp"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
clitypes "github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/crypto"
)
func MakeGet(repo clitypes.ConfigRepo, initClient CredentialClientInit) *cobra.Command {
getCmd := &cobra.Command{
Use: "get",
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.`,
Run: func(cmd *cobra.Command, args []string) {
var (
copyPass bool
cleancb bool
prompt survey.Prompt
)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
client := initClient(ctx)
masterpass, cfg, err := repo.OpenConfig()
check(err)
cred := selectCredential(client)
fmt.Println(cred)
check(clipboard.WriteAll(string(cred.Primary)))
fmt.Println("Wrote primary user key to clipboard.")
key := cfg.GetString(clitypes.KeyPrivateKey)
passkey := crypto.GeneratePBKDF2Key([]byte(masterpass), []byte(key))
prompt = &survey.Confirm{Message: "Copy password to clipboard?", Default: true}
check(survey.AskOne(prompt, &copyPass, 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.")
}
}
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
}

View file

@ -0,0 +1,77 @@
package commands
import (
"context"
"fmt"
"sort"
"time"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/services/credentials/types"
)
func MakeList(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*20)
defer cancel()
mdch, errch := initClient(ctx).GetAllMetadata(ctx, sourceHost)
mds := map[string][]types.Metadata{}
fmt.Println()
receive:
for {
select {
case <-ctx.Done():
check(ctx.Err())
case err := <-errch:
check(err)
case md, ok := <-mdch:
if !ok {
break receive
}
mds[md.SourceHost] = append(mds[md.SourceHost], md)
}
}
sources := []string{}
for source := range mds {
sources = append(sources, source)
}
sort.Strings(sources)
prompt := &survey.Select{
Message: "Source host:",
Options: sources,
PageSize: 20,
VimMode: true,
}
var source string
check(survey.AskOne(prompt, &source, nil))
for _, md := range mds[source] {
fmt.Println(md)
}
fmt.Println("Done listing.")
},
}
return listCmd
}

View file

@ -0,0 +1,190 @@
package commands
import (
"context"
"encoding/base64"
"fmt"
"os"
"time"
"github.com/atotto/clipboard"
"github.com/pquerna/otp/totp"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
clitypes "github.com/mitchell/selfpass/services/cli/types"
"github.com/mitchell/selfpass/services/credentials/types"
"github.com/mitchell/selfpass/services/crypto"
)
func MakeUpdate(repo clitypes.ConfigRepo, initClient CredentialClientInit) *cobra.Command {
var length uint
var numbers bool
var specials bool
updateCmd := &cobra.Command{
Use: "update",
Short: "Update a credential in Selfpass",
Long: `Update a credential in Selfpass, and save it to the server after encrypting the
password.`,
Run: func(_ *cobra.Command, args []string) {
var (
newpass bool
otp bool
cleancb bool
prompt survey.Prompt
ci types.CredentialInput
)
masterpass, cfg, err := repo.OpenConfig()
check(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
client := initClient(ctx)
cred := selectCredential(client)
mdqs := []*survey.Question{
{
Name: "primary",
Prompt: &survey.Input{
Message: "Primary user key:",
Default: cred.Primary,
},
},
{
Name: "sourceHost",
Prompt: &survey.Input{
Message: "Source host:",
Default: cred.SourceHost,
},
},
{
Name: "loginURL",
Prompt: &survey.Input{
Message: "Login url:",
Default: cred.LoginURL,
},
},
{
Name: "tag",
Prompt: &survey.Input{
Message: "Tag:",
Default: cred.Tag,
},
},
}
cqs := []*survey.Question{
{
Name: "username",
Prompt: &survey.Input{
Message: "Username:",
Default: cred.Username,
},
},
{
Name: "email",
Prompt: &survey.Input{
Message: "Email:",
Default: cred.Email,
},
},
}
check(survey.Ask(mdqs, &ci.MetadataInput))
check(survey.Ask(cqs, &ci))
ci.Password = cred.Password
ci.OTPSecret = cred.OTPSecret
key := cfg.GetString(clitypes.KeyPrivateKey)
keypass := crypto.GeneratePBKDF2Key([]byte(masterpass), []byte(key))
prompt = &survey.Confirm{Message: "Do you want a new password?", Default: true}
check(survey.AskOne(prompt, &newpass, nil))
if newpass {
var randpass bool
prompt = &survey.Confirm{Message: "Do you want a random password?", Default: true}
check(survey.AskOne(prompt, &randpass, nil))
if randpass {
ci.Password = crypto.GeneratePassword(int(length), numbers, specials)
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 password: "}
check(survey.AskOne(prompt, &cpass, nil))
if ci.Password != cpass {
fmt.Println("passwords didn't match")
os.Exit(1)
}
}
cipherpass, err := crypto.CBCEncrypt(keypass, []byte(ci.Password))
check(err)
ci.Password = base64.StdEncoding.EncodeToString(cipherpass)
}
prompt = &survey.Confirm{Message: "Do you want to set a new 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, &copyotp, nil))
if copyotp {
otp, err := totp.GenerateCode(secret, time.Now())
check(err)
check(clipboard.WriteAll(otp))
}
}
ctx, cancel = context.WithTimeout(context.Background(), time.Second*25)
defer cancel()
c, err := initClient(ctx).Update(ctx, cred.ID, ci)
check(err)
fmt.Println(c)
prompt = &survey.Confirm{Message: "Do you want to clear the clipboard?", Default: true}
check(survey.AskOne(prompt, &cleancb, nil))
if cleancb {
check(clipboard.WriteAll(" "))
}
},
}
updateCmd.Flags().BoolVarP(&numbers, "numbers", "n", true, "use numbers in the generated password")
updateCmd.Flags().BoolVarP(&specials, "specials", "s", false, "use special characters in the generated password")
updateCmd.Flags().UintVarP(&length, "length", "l", 32, "length of the generated password")
return updateCmd
}

View file

@ -0,0 +1,78 @@
package endpoints
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/mitchell/selfpass/services/credentials/types"
)
func MakeCreateEndpoint(svc types.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
r := request.(types.CredentialInput)
return svc.Create(ctx, r)
}
}
func MakeDeleteEndpoint(svc types.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
r := request.(IDRequest)
return nil, svc.Delete(ctx, r.ID)
}
}
func MakeGetEndpoint(svc types.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
r := request.(IDRequest)
return svc.Get(ctx, r.ID)
}
}
func MakeGetAllMetadataEndpoint(svc types.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
r := request.(GetAllMetadataRequest)
mdch, errch := svc.GetAllMetadata(ctx, r.SourceHost)
return MetadataStream{
Metadata: mdch,
Errors: errch,
}, nil
}
}
func MakeUpdateEndpoint(svc types.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
r := request.(UpdateRequest)
return svc.Update(ctx, r.ID, r.Credential)
}
}
func MakeDumpEndpoint(svc types.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
contents, err := svc.DumpDB(ctx)
return DumpResponse{Contents: contents}, err
}
}
type DumpResponse struct {
Contents []byte
}
type IDRequest struct {
ID string
}
type GetAllMetadataRequest struct {
SourceHost string
}
type MetadataStream struct {
Metadata <-chan types.Metadata
Errors chan error
}
type UpdateRequest struct {
ID string
Credential types.CredentialInput
}

View file

@ -0,0 +1,114 @@
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/log"
"github.com/mitchell/selfpass/services/credentials/types"
)
func NewServiceLogger(l log.Logger, next types.Service) ServiceLogger {
return ServiceLogger{
l: l,
next: next,
}
}
type ServiceLogger struct {
l log.Logger
next types.Service
}
func (svc ServiceLogger) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
defer func(begin time.Time) {
_ = svc.l.Log(
"service", "Credentials",
"method", "GetAllMetadata",
"input", sourceHost,
"output", "channel",
"err", "channel",
"took", time.Since(begin),
)
}(time.Now())
return svc.next.GetAllMetadata(ctx, sourceHost)
}
func (svc ServiceLogger) Get(ctx context.Context, id string) (output types.Credential, err error) {
defer func(begin time.Time) {
_ = svc.l.Log(
"service", "Credentials",
"method", "Get",
"input", id,
"output", output,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
output, err = svc.next.Get(ctx, id)
return output, err
}
func (svc ServiceLogger) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
defer func(begin time.Time) {
_ = svc.l.Log(
"service", "Credentials",
"method", "Create",
"input", ci,
"output", output,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
output, err = svc.next.Create(ctx, ci)
return output, err
}
func (svc ServiceLogger) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
defer func(begin time.Time) {
_ = svc.l.Log(
"service", "Credentials",
"method", "Update",
"input", []interface{}{id, ci},
"output", output,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
output, err = svc.next.Update(ctx, id, ci)
return output, err
}
func (svc ServiceLogger) Delete(ctx context.Context, id string) (err error) {
defer func(begin time.Time) {
_ = svc.l.Log(
"service", "Credentials",
"method", "Delete",
"input", id,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
err = svc.next.Delete(ctx, id)
return err
}
func (svc ServiceLogger) DumpDB(ctx context.Context) (output []byte, err error) {
defer func(begin time.Time) {
_ = svc.l.Log(
"service", "Credentials",
"method", "Dump",
"output", output,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
output, err = svc.next.DumpDB(ctx)
return output, err
}

View file

@ -0,0 +1,867 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: credentials/protobuf/service.proto
package protobuf
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type DeleteResponse struct {
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DeleteResponse) Reset() { *m = DeleteResponse{} }
func (m *DeleteResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteResponse) ProtoMessage() {}
func (*DeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{0}
}
func (m *DeleteResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteResponse.Unmarshal(m, b)
}
func (m *DeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DeleteResponse.Marshal(b, m, deterministic)
}
func (m *DeleteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DeleteResponse.Merge(m, src)
}
func (m *DeleteResponse) XXX_Size() int {
return xxx_messageInfo_DeleteResponse.Size(m)
}
func (m *DeleteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DeleteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo
func (m *DeleteResponse) GetSuccess() bool {
if m != nil {
return m.Success
}
return false
}
type GetAllMetadataRequest struct {
SourceHost string `protobuf:"bytes,1,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetAllMetadataRequest) Reset() { *m = GetAllMetadataRequest{} }
func (m *GetAllMetadataRequest) String() string { return proto.CompactTextString(m) }
func (*GetAllMetadataRequest) ProtoMessage() {}
func (*GetAllMetadataRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{1}
}
func (m *GetAllMetadataRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetAllMetadataRequest.Unmarshal(m, b)
}
func (m *GetAllMetadataRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetAllMetadataRequest.Marshal(b, m, deterministic)
}
func (m *GetAllMetadataRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetAllMetadataRequest.Merge(m, src)
}
func (m *GetAllMetadataRequest) XXX_Size() int {
return xxx_messageInfo_GetAllMetadataRequest.Size(m)
}
func (m *GetAllMetadataRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetAllMetadataRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetAllMetadataRequest proto.InternalMessageInfo
func (m *GetAllMetadataRequest) GetSourceHost() string {
if m != nil {
return m.SourceHost
}
return ""
}
type IdRequest struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *IdRequest) Reset() { *m = IdRequest{} }
func (m *IdRequest) String() string { return proto.CompactTextString(m) }
func (*IdRequest) ProtoMessage() {}
func (*IdRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{2}
}
func (m *IdRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_IdRequest.Unmarshal(m, b)
}
func (m *IdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_IdRequest.Marshal(b, m, deterministic)
}
func (m *IdRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_IdRequest.Merge(m, src)
}
func (m *IdRequest) XXX_Size() int {
return xxx_messageInfo_IdRequest.Size(m)
}
func (m *IdRequest) XXX_DiscardUnknown() {
xxx_messageInfo_IdRequest.DiscardUnknown(m)
}
var xxx_messageInfo_IdRequest proto.InternalMessageInfo
func (m *IdRequest) GetId() string {
if m != nil {
return m.Id
}
return ""
}
type UpdateRequest struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Credential *CredentialRequest `protobuf:"bytes,2,opt,name=credential,proto3" json:"credential,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UpdateRequest) Reset() { *m = UpdateRequest{} }
func (m *UpdateRequest) String() string { return proto.CompactTextString(m) }
func (*UpdateRequest) ProtoMessage() {}
func (*UpdateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{3}
}
func (m *UpdateRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UpdateRequest.Unmarshal(m, b)
}
func (m *UpdateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UpdateRequest.Marshal(b, m, deterministic)
}
func (m *UpdateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_UpdateRequest.Merge(m, src)
}
func (m *UpdateRequest) XXX_Size() int {
return xxx_messageInfo_UpdateRequest.Size(m)
}
func (m *UpdateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_UpdateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_UpdateRequest proto.InternalMessageInfo
func (m *UpdateRequest) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *UpdateRequest) GetCredential() *CredentialRequest {
if m != nil {
return m.Credential
}
return nil
}
type DumpResponse struct {
Contents []byte `protobuf:"bytes,1,opt,name=contents,proto3" json:"contents,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DumpResponse) Reset() { *m = DumpResponse{} }
func (m *DumpResponse) String() string { return proto.CompactTextString(m) }
func (*DumpResponse) ProtoMessage() {}
func (*DumpResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{4}
}
func (m *DumpResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DumpResponse.Unmarshal(m, b)
}
func (m *DumpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DumpResponse.Marshal(b, m, deterministic)
}
func (m *DumpResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DumpResponse.Merge(m, src)
}
func (m *DumpResponse) XXX_Size() int {
return xxx_messageInfo_DumpResponse.Size(m)
}
func (m *DumpResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DumpResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DumpResponse proto.InternalMessageInfo
func (m *DumpResponse) GetContents() []byte {
if m != nil {
return m.Contents
}
return nil
}
type EmptyRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EmptyRequest) Reset() { *m = EmptyRequest{} }
func (m *EmptyRequest) String() string { return proto.CompactTextString(m) }
func (*EmptyRequest) ProtoMessage() {}
func (*EmptyRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{5}
}
func (m *EmptyRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EmptyRequest.Unmarshal(m, b)
}
func (m *EmptyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EmptyRequest.Marshal(b, m, deterministic)
}
func (m *EmptyRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_EmptyRequest.Merge(m, src)
}
func (m *EmptyRequest) XXX_Size() int {
return xxx_messageInfo_EmptyRequest.Size(m)
}
func (m *EmptyRequest) XXX_DiscardUnknown() {
xxx_messageInfo_EmptyRequest.DiscardUnknown(m)
}
var xxx_messageInfo_EmptyRequest proto.InternalMessageInfo
type Metadata 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"`
UpdatedAt *timestamp.Timestamp `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
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:"-"`
}
func (m *Metadata) Reset() { *m = Metadata{} }
func (m *Metadata) String() string { return proto.CompactTextString(m) }
func (*Metadata) ProtoMessage() {}
func (*Metadata) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{6}
}
func (m *Metadata) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Metadata.Unmarshal(m, b)
}
func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Metadata.Marshal(b, m, deterministic)
}
func (m *Metadata) XXX_Merge(src proto.Message) {
xxx_messageInfo_Metadata.Merge(m, src)
}
func (m *Metadata) XXX_Size() int {
return xxx_messageInfo_Metadata.Size(m)
}
func (m *Metadata) XXX_DiscardUnknown() {
xxx_messageInfo_Metadata.DiscardUnknown(m)
}
var xxx_messageInfo_Metadata proto.InternalMessageInfo
func (m *Metadata) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Metadata) GetCreatedAt() *timestamp.Timestamp {
if m != nil {
return m.CreatedAt
}
return nil
}
func (m *Metadata) GetUpdatedAt() *timestamp.Timestamp {
if m != nil {
return m.UpdatedAt
}
return nil
}
func (m *Metadata) GetPrimary() string {
if m != nil {
return m.Primary
}
return ""
}
func (m *Metadata) GetSourceHost() string {
if m != nil {
return m.SourceHost
}
return ""
}
func (m *Metadata) GetLoginUrl() string {
if m != nil {
return m.LoginUrl
}
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"`
UpdatedAt *timestamp.Timestamp `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
Primary string `protobuf:"bytes,4,opt,name=primary,proto3" json:"primary,omitempty"`
Username string `protobuf:"bytes,5,opt,name=username,proto3" json:"username,omitempty"`
Email string `protobuf:"bytes,6,opt,name=email,proto3" json:"email,omitempty"`
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"`
OtpSecret string `protobuf:"bytes,11,opt,name=otp_secret,json=otpSecret,proto3" json:"otp_secret,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Credential) Reset() { *m = Credential{} }
func (m *Credential) String() string { return proto.CompactTextString(m) }
func (*Credential) ProtoMessage() {}
func (*Credential) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{7}
}
func (m *Credential) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Credential.Unmarshal(m, b)
}
func (m *Credential) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Credential.Marshal(b, m, deterministic)
}
func (m *Credential) XXX_Merge(src proto.Message) {
xxx_messageInfo_Credential.Merge(m, src)
}
func (m *Credential) XXX_Size() int {
return xxx_messageInfo_Credential.Size(m)
}
func (m *Credential) XXX_DiscardUnknown() {
xxx_messageInfo_Credential.DiscardUnknown(m)
}
var xxx_messageInfo_Credential proto.InternalMessageInfo
func (m *Credential) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Credential) GetCreatedAt() *timestamp.Timestamp {
if m != nil {
return m.CreatedAt
}
return nil
}
func (m *Credential) GetUpdatedAt() *timestamp.Timestamp {
if m != nil {
return m.UpdatedAt
}
return nil
}
func (m *Credential) GetPrimary() string {
if m != nil {
return m.Primary
}
return ""
}
func (m *Credential) GetUsername() string {
if m != nil {
return m.Username
}
return ""
}
func (m *Credential) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
func (m *Credential) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
func (m *Credential) GetSourceHost() string {
if m != nil {
return m.SourceHost
}
return ""
}
func (m *Credential) GetLoginUrl() string {
if m != nil {
return m.LoginUrl
}
return ""
}
func (m *Credential) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *Credential) GetOtpSecret() string {
if m != nil {
return m.OtpSecret
}
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"`
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
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"`
OtpSecret string `protobuf:"bytes,8,opt,name=otp_secret,json=otpSecret,proto3" json:"otp_secret,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CredentialRequest) Reset() { *m = CredentialRequest{} }
func (m *CredentialRequest) String() string { return proto.CompactTextString(m) }
func (*CredentialRequest) ProtoMessage() {}
func (*CredentialRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ad34efc7bbd96e69, []int{8}
}
func (m *CredentialRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CredentialRequest.Unmarshal(m, b)
}
func (m *CredentialRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CredentialRequest.Marshal(b, m, deterministic)
}
func (m *CredentialRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CredentialRequest.Merge(m, src)
}
func (m *CredentialRequest) XXX_Size() int {
return xxx_messageInfo_CredentialRequest.Size(m)
}
func (m *CredentialRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CredentialRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CredentialRequest proto.InternalMessageInfo
func (m *CredentialRequest) GetPrimary() string {
if m != nil {
return m.Primary
}
return ""
}
func (m *CredentialRequest) GetUsername() string {
if m != nil {
return m.Username
}
return ""
}
func (m *CredentialRequest) GetEmail() string {
if m != nil {
return m.Email
}
return ""
}
func (m *CredentialRequest) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
func (m *CredentialRequest) GetSourceHost() string {
if m != nil {
return m.SourceHost
}
return ""
}
func (m *CredentialRequest) GetLoginUrl() string {
if m != nil {
return m.LoginUrl
}
return ""
}
func (m *CredentialRequest) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *CredentialRequest) GetOtpSecret() string {
if m != nil {
return m.OtpSecret
}
return ""
}
func init() {
proto.RegisterType((*DeleteResponse)(nil), "selfpass.credentials.DeleteResponse")
proto.RegisterType((*GetAllMetadataRequest)(nil), "selfpass.credentials.GetAllMetadataRequest")
proto.RegisterType((*IdRequest)(nil), "selfpass.credentials.IdRequest")
proto.RegisterType((*UpdateRequest)(nil), "selfpass.credentials.UpdateRequest")
proto.RegisterType((*DumpResponse)(nil), "selfpass.credentials.DumpResponse")
proto.RegisterType((*EmptyRequest)(nil), "selfpass.credentials.EmptyRequest")
proto.RegisterType((*Metadata)(nil), "selfpass.credentials.Metadata")
proto.RegisterType((*Credential)(nil), "selfpass.credentials.Credential")
proto.RegisterType((*CredentialRequest)(nil), "selfpass.credentials.CredentialRequest")
}
func init() { proto.RegisterFile("credentials/protobuf/service.proto", fileDescriptor_ad34efc7bbd96e69) }
var fileDescriptor_ad34efc7bbd96e69 = []byte{
// 561 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x95, 0x4d, 0x6f, 0xd3, 0x4c,
0x10, 0xc7, 0xe5, 0xa4, 0x4d, 0xed, 0x69, 0x9f, 0xe8, 0x61, 0x55, 0x24, 0xcb, 0x15, 0x24, 0x32,
0x48, 0x54, 0x45, 0x72, 0x51, 0xb9, 0xc0, 0x31, 0xb4, 0x28, 0xe5, 0x80, 0x90, 0x1c, 0x7a, 0xe1,
0x12, 0x6d, 0xed, 0x69, 0x6a, 0xc9, 0xf6, 0x9a, 0xdd, 0x31, 0xa8, 0x1f, 0x8c, 0x0f, 0x05, 0x1f,
0x81, 0x13, 0xf2, 0xfa, 0x25, 0x2f, 0x75, 0x9a, 0x48, 0x5c, 0xb8, 0xed, 0x8c, 0xff, 0xb3, 0x9a,
0xdf, 0xec, 0xee, 0xdf, 0xe0, 0x06, 0x12, 0x43, 0x4c, 0x29, 0xe2, 0xb1, 0x3a, 0xcd, 0xa4, 0x20,
0x71, 0x9d, 0xdf, 0x9c, 0x2a, 0x94, 0xdf, 0xa2, 0x00, 0x3d, 0x9d, 0x60, 0x87, 0x0a, 0xe3, 0x9b,
0x8c, 0x2b, 0xe5, 0x2d, 0x88, 0x9d, 0xc1, 0x4c, 0x88, 0x59, 0x8c, 0xf3, 0x22, 0x8a, 0x12, 0x54,
0xc4, 0x93, 0xac, 0x2c, 0x73, 0x4f, 0xa0, 0x7f, 0x81, 0x31, 0x12, 0xfa, 0xa8, 0x32, 0x91, 0x2a,
0x64, 0x36, 0xec, 0xa9, 0x3c, 0x08, 0x50, 0x29, 0xdb, 0x18, 0x1a, 0xc7, 0xa6, 0x5f, 0x87, 0xee,
0x1b, 0x78, 0x3c, 0x46, 0x1a, 0xc5, 0xf1, 0x47, 0x24, 0x1e, 0x72, 0xe2, 0x3e, 0x7e, 0xcd, 0x51,
0x11, 0x1b, 0xc0, 0xbe, 0x12, 0xb9, 0x0c, 0x70, 0x7a, 0x2b, 0x14, 0xe9, 0x32, 0xcb, 0x87, 0x32,
0x75, 0x29, 0x14, 0xb9, 0x47, 0x60, 0x7d, 0x08, 0x6b, 0x75, 0x1f, 0x3a, 0x51, 0x58, 0x89, 0x3a,
0x51, 0xe8, 0xde, 0xc2, 0x7f, 0x57, 0x59, 0xc8, 0x8b, 0x16, 0x5a, 0x05, 0x6c, 0x0c, 0x30, 0x67,
0xb2, 0x3b, 0x43, 0xe3, 0x78, 0xff, 0xec, 0x85, 0xd7, 0xc6, 0xeb, 0x9d, 0x37, 0xeb, 0x6a, 0x33,
0x7f, 0xa1, 0xd4, 0x3d, 0x81, 0x83, 0x8b, 0x3c, 0xc9, 0x1a, 0x54, 0x07, 0xcc, 0x40, 0xa4, 0x84,
0x29, 0x95, 0xac, 0x07, 0x7e, 0x13, 0xbb, 0x7d, 0x38, 0x78, 0x9f, 0x64, 0x74, 0x57, 0xed, 0xe3,
0xfe, 0x36, 0xc0, 0xac, 0xb9, 0xef, 0x75, 0xf8, 0x56, 0x77, 0xc8, 0x09, 0xc3, 0x29, 0xa7, 0xaa,
0x43, 0xc7, 0x2b, 0x67, 0xef, 0xd5, 0xb3, 0xf7, 0x3e, 0xd7, 0xb3, 0xf7, 0xad, 0x4a, 0x3d, 0xa2,
0xa2, 0x34, 0xd7, 0xf4, 0xba, 0xb4, 0xbb, 0xb9, 0xb4, 0x52, 0x8f, 0xa8, 0x38, 0xa9, 0x4c, 0x46,
0x09, 0x97, 0x77, 0xf6, 0x8e, 0x6e, 0xa5, 0x0e, 0x57, 0x0f, 0x64, 0x77, 0xf5, 0x40, 0xd8, 0x11,
0x58, 0xb1, 0x98, 0x45, 0xe9, 0x34, 0x97, 0xb1, 0xdd, 0xd3, 0x9f, 0x4d, 0x9d, 0xb8, 0x92, 0x31,
0xfb, 0x1f, 0xba, 0xc4, 0x67, 0xf6, 0x9e, 0x4e, 0x17, 0x4b, 0xf7, 0x67, 0x07, 0x60, 0x3e, 0xda,
0x7f, 0x1e, 0xdf, 0x01, 0x33, 0x57, 0x28, 0x53, 0x9e, 0x60, 0xc5, 0xde, 0xc4, 0xec, 0x10, 0x76,
0x31, 0xe1, 0x51, 0x4d, 0x5d, 0x06, 0x45, 0x45, 0x71, 0x97, 0xbe, 0x0b, 0x19, 0x56, 0xdc, 0x4d,
0xbc, 0x3a, 0x4c, 0xf3, 0xe1, 0x61, 0x5a, 0xed, 0xc3, 0x84, 0x66, 0x98, 0xec, 0x09, 0x80, 0xa0,
0x6c, 0xaa, 0x30, 0x90, 0x48, 0xf6, 0xbe, 0xfe, 0x60, 0x09, 0xca, 0x26, 0x3a, 0xe1, 0xfe, 0x32,
0xe0, 0xd1, 0xbd, 0x6b, 0xbc, 0x08, 0x6b, 0xac, 0x87, 0xed, 0xac, 0x83, 0xed, 0xae, 0x83, 0xdd,
0x79, 0x18, 0xf6, 0x6f, 0x6f, 0xce, 0x0a, 0xac, 0xb9, 0x02, 0x7b, 0xf6, 0xa3, 0xbb, 0x08, 0x3b,
0x29, 0x1d, 0x8d, 0x4d, 0xa1, 0xbf, 0x6c, 0x34, 0xec, 0x65, 0xfb, 0x73, 0x6f, 0xb5, 0x23, 0xe7,
0x69, 0xbb, 0xb8, 0x96, 0xbd, 0x32, 0xd8, 0x25, 0x74, 0xc7, 0x48, 0x6c, 0xd0, 0x2e, 0x6c, 0xac,
0xca, 0x19, 0x6e, 0x72, 0x19, 0x36, 0x81, 0xde, 0xb9, 0xbe, 0xcc, 0x6c, 0x5b, 0x47, 0xda, 0x62,
0xd3, 0x4f, 0xd0, 0x2b, 0x1d, 0x91, 0x3d, 0x6b, 0xd7, 0x2e, 0xf9, 0xe5, 0x76, 0x1b, 0x96, 0x2e,
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.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// CredentialServiceClient is the client API for CredentialService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CredentialServiceClient interface {
GetAllMetadata(ctx context.Context, in *GetAllMetadataRequest, opts ...grpc.CallOption) (CredentialService_GetAllMetadataClient, error)
Get(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*Credential, error)
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)
}
type credentialServiceClient struct {
cc *grpc.ClientConn
}
func NewCredentialServiceClient(cc *grpc.ClientConn) CredentialServiceClient {
return &credentialServiceClient{cc}
}
func (c *credentialServiceClient) GetAllMetadata(ctx context.Context, in *GetAllMetadataRequest, opts ...grpc.CallOption) (CredentialService_GetAllMetadataClient, error) {
stream, err := c.cc.NewStream(ctx, &_CredentialService_serviceDesc.Streams[0], "/selfpass.credentials.CredentialService/GetAllMetadata", opts...)
if err != nil {
return nil, err
}
x := &credentialServiceGetAllMetadataClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type CredentialService_GetAllMetadataClient interface {
Recv() (*Metadata, error)
grpc.ClientStream
}
type credentialServiceGetAllMetadataClient struct {
grpc.ClientStream
}
func (x *credentialServiceGetAllMetadataClient) Recv() (*Metadata, error) {
m := new(Metadata)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *credentialServiceClient) Get(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*Credential, error) {
out := new(Credential)
err := c.cc.Invoke(ctx, "/selfpass.credentials.CredentialService/Get", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *credentialServiceClient) Create(ctx context.Context, in *CredentialRequest, opts ...grpc.CallOption) (*Credential, error) {
out := new(Credential)
err := c.cc.Invoke(ctx, "/selfpass.credentials.CredentialService/Create", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *credentialServiceClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Credential, error) {
out := new(Credential)
err := c.cc.Invoke(ctx, "/selfpass.credentials.CredentialService/Update", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *credentialServiceClient) Delete(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*DeleteResponse, error) {
out := new(DeleteResponse)
err := c.cc.Invoke(ctx, "/selfpass.credentials.CredentialService/Delete", 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
Get(context.Context, *IdRequest) (*Credential, error)
Create(context.Context, *CredentialRequest) (*Credential, error)
Update(context.Context, *UpdateRequest) (*Credential, error)
Delete(context.Context, *IdRequest) (*DeleteResponse, error)
}
// UnimplementedCredentialServiceServer can be embedded to have forward compatible implementations.
type UnimplementedCredentialServiceServer struct {
}
func (*UnimplementedCredentialServiceServer) GetAllMetadata(req *GetAllMetadataRequest, srv CredentialService_GetAllMetadataServer) error {
return status.Errorf(codes.Unimplemented, "method GetAllMetadata not implemented")
}
func (*UnimplementedCredentialServiceServer) Get(ctx context.Context, req *IdRequest) (*Credential, error) {
return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
}
func (*UnimplementedCredentialServiceServer) Create(ctx context.Context, req *CredentialRequest) (*Credential, error) {
return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
}
func (*UnimplementedCredentialServiceServer) Update(ctx context.Context, req *UpdateRequest) (*Credential, error) {
return nil, status.Errorf(codes.Unimplemented, "method Update not implemented")
}
func (*UnimplementedCredentialServiceServer) Delete(ctx context.Context, req *IdRequest) (*DeleteResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
}
func RegisterCredentialServiceServer(s *grpc.Server, srv CredentialServiceServer) {
s.RegisterService(&_CredentialService_serviceDesc, srv)
}
func _CredentialService_GetAllMetadata_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(GetAllMetadataRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(CredentialServiceServer).GetAllMetadata(m, &credentialServiceGetAllMetadataServer{stream})
}
type CredentialService_GetAllMetadataServer interface {
Send(*Metadata) error
grpc.ServerStream
}
type credentialServiceGetAllMetadataServer struct {
grpc.ServerStream
}
func (x *credentialServiceGetAllMetadataServer) Send(m *Metadata) error {
return x.ServerStream.SendMsg(m)
}
func _CredentialService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(IdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).Get(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/selfpass.credentials.CredentialService/Get",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).Get(ctx, req.(*IdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CredentialService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CredentialRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).Create(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/selfpass.credentials.CredentialService/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).Create(ctx, req.(*CredentialRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CredentialService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).Update(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/selfpass.credentials.CredentialService/Update",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).Update(ctx, req.(*UpdateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CredentialService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(IdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CredentialServiceServer).Delete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/selfpass.credentials.CredentialService/Delete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CredentialServiceServer).Delete(ctx, req.(*IdRequest))
}
return interceptor(ctx, in, info, handler)
}
var _CredentialService_serviceDesc = grpc.ServiceDesc{
ServiceName: "selfpass.credentials.CredentialService",
HandlerType: (*CredentialServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Get",
Handler: _CredentialService_Get_Handler,
},
{
MethodName: "Create",
Handler: _CredentialService_Create_Handler,
},
{
MethodName: "Update",
Handler: _CredentialService_Update_Handler,
},
{
MethodName: "Delete",
Handler: _CredentialService_Delete_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "GetAllMetadata",
Handler: _CredentialService_GetAllMetadata_Handler,
ServerStreams: true,
},
},
Metadata: "credentials/protobuf/service.proto",
}

View file

@ -0,0 +1,75 @@
syntax = "proto3";
package selfpass.credentials;
option go_package = "protobuf";
import "google/protobuf/timestamp.proto";
service CredentialService {
rpc GetAllMetadata (GetAllMetadataRequest) returns (stream Metadata);
rpc Get (IdRequest) returns (Credential);
rpc Create (CredentialRequest) returns (Credential);
rpc Update (UpdateRequest) returns (Credential);
rpc Delete (IdRequest) returns (DeleteResponse);
// rpc Dump (EmptyRequest) returns (DumpResponse);
}
message DeleteResponse {
bool success = 1;
}
message GetAllMetadataRequest {
string source_host = 1;
}
message IdRequest {
string id = 1;
}
message UpdateRequest {
string id = 1;
CredentialRequest credential = 2;
}
message DumpResponse {
bytes contents = 1;
}
message EmptyRequest {
}
message Metadata {
string id = 1;
google.protobuf.Timestamp created_at = 2;
google.protobuf.Timestamp updated_at = 3;
string primary = 4;
string source_host = 5;
string login_url = 6;
string tag = 7;
}
message Credential {
string id = 1;
google.protobuf.Timestamp created_at = 2;
google.protobuf.Timestamp updated_at = 3;
string primary = 4;
string username = 5;
string email = 6;
string password = 7;
string source_host = 8;
string login_url = 9;
string tag = 10;
string otp_secret = 11;
}
message CredentialRequest {
string primary = 1;
string username = 2;
string email = 3;
string password = 4;
string source_host = 5;
string login_url = 6;
string tag = 7;
string otp_secret = 8;
}

View file

@ -0,0 +1,136 @@
package repositories
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"github.com/mitchell/selfpass/services/credentials/endpoints"
"github.com/mitchell/selfpass/services/credentials/protobuf"
"github.com/mitchell/selfpass/services/credentials/transport"
"github.com/mitchell/selfpass/services/credentials/types"
)
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},
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{
tls.CurveP256,
},
})
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) {
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) {
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
}

View file

@ -0,0 +1,81 @@
package repositories
import (
"context"
"github.com/mediocregopher/radix/v3"
"github.com/mitchell/selfpass/services/credentials/types"
)
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 RedisConn struct {
p *radix.Pool
}
func (conn RedisConn) GetAllMetadata(ctx context.Context, sourceHost string, errch chan<- error) (output <-chan types.Metadata) {
mdch := make(chan types.Metadata, 1)
go func() {
defer close(mdch)
var key string
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
}
}()
return mdch
}
func (conn RedisConn) Get(ctx context.Context, id string) (output types.Credential, err error) {
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.ID, c))
return err
}
func (conn RedisConn) Delete(ctx context.Context, id string) (err error) {
err = conn.p.Do(radix.Cmd(nil, del, id))
return err
}
func (conn RedisConn) DumpDB(ctx context.Context) (bs []byte, err error) {
bs = []byte{}
if err := conn.p.Do(radix.Cmd(&bs, "DUMP")); err != nil {
return nil, err
}
return bs, nil
}
const (
dash = "-"
star = "*"
scan = "SCAN"
hGetAll = "HGETALL"
hMSet = "HMSET"
del = "DEL"
)

View file

@ -0,0 +1,118 @@
package service
import (
"context"
"fmt"
"time"
"github.com/mitchell/selfpass/services/credentials/types"
)
func NewCredentials(repo types.CredentialRepo) Credentials {
return Credentials{
repo: repo,
}
}
type Credentials struct {
repo types.CredentialRepo
}
func (svc Credentials) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
errch = make(chan error, 1)
output = svc.repo.GetAllMetadata(ctx, sourceHost, errch)
return output, errch
}
func (svc Credentials) Get(ctx context.Context, id string) (output types.Credential, err error) {
if id == "" {
return output, fmt.Errorf("%s must specify an id", types.InvalidArgument)
}
return svc.repo.Get(ctx, id)
}
func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
if err = validateCredentialInput(ci); err != nil {
return output, err
}
var c types.Credential
c.ID = generateID(ci)
c.CreatedAt = time.Now()
c.UpdatedAt = time.Now()
c.Primary = ci.Primary
c.LoginURL = ci.LoginURL
c.SourceHost = ci.SourceHost
c.Username = ci.Username
c.Email = ci.Email
c.Password = ci.Password
c.OTPSecret = ci.OTPSecret
c.Tag = ci.Tag
err = svc.repo.Put(ctx, c)
return c, err
}
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)
}
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
}
if id == "" {
return output, fmt.Errorf("%s must specify an id", types.InvalidArgument)
}
c, err := svc.repo.Get(ctx, id)
if err != nil {
return output, err
}
c.ID = generateID(ci)
c.UpdatedAt = time.Now()
c.Primary = ci.Primary
c.LoginURL = ci.LoginURL
c.SourceHost = ci.SourceHost
c.Password = ci.Password
c.OTPSecret = ci.OTPSecret
c.Email = ci.Email
c.Username = ci.Username
c.Tag = ci.Tag
return c, svc.repo.Put(ctx, c)
}
func (svc Credentials) Delete(ctx context.Context, id string) (err error) {
if id == "" {
return fmt.Errorf("%s must specify an id", types.InvalidArgument)
}
return svc.repo.Delete(ctx, id)
}
func (svc Credentials) DumpDB(ctx context.Context) (bs []byte, err error) {
return svc.repo.DumpDB(ctx)
}

View file

@ -0,0 +1,254 @@
package transport
import (
"context"
"github.com/golang/protobuf/ptypes"
"github.com/mitchell/selfpass/services/credentials/endpoints"
"github.com/mitchell/selfpass/services/credentials/protobuf"
"github.com/mitchell/selfpass/services/credentials/types"
)
func decodeGetAllMetadataRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(protobuf.GetAllMetadataRequest)
return endpoints.GetAllMetadataRequest{
SourceHost: r.SourceHost,
}, 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{
Contents: r.Contents,
}, nil
}
func encodeMetadataStreamResponse(ctx context.Context, response interface{}) (interface{}, error) {
r := response.(endpoints.MetadataStream)
pbmdch := make(chan protobuf.Metadata, 1)
go func() {
defer close(pbmdch)
for md := range r.Metadata {
createdAt, err := ptypes.TimestampProto(md.CreatedAt)
if err != nil {
r.Errors <- err
return
}
updatedAt, err := ptypes.TimestampProto(md.UpdatedAt)
if err != nil {
r.Errors <- err
return
}
pbmdch <- protobuf.Metadata{
Id: md.ID,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
SourceHost: md.SourceHost,
Primary: md.Primary,
LoginUrl: md.LoginURL,
Tag: md.Tag,
}
}
}()
return ProtobufMetadataStream{
Metadata: pbmdch,
Errors: r.Errors,
}, nil
}
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,
Password: r.Password,
OTPSecret: r.OtpSecret,
}, nil
}
func EncodeCredentialRequest(r types.CredentialInput) protobuf.CredentialRequest {
return protobuf.CredentialRequest{
Primary: r.Primary,
Username: r.Username,
Email: r.Email,
Password: r.Password,
OtpSecret: r.OTPSecret,
SourceHost: r.SourceHost,
LoginUrl: r.LoginURL,
Tag: r.Tag,
}
}
func encodeCredentialResponse(ctx context.Context, response interface{}) (interface{}, error) {
r := response.(types.Credential)
createdAt, err := ptypes.TimestampProto(r.CreatedAt)
if err != nil {
return nil, err
}
updatedAt, err := ptypes.TimestampProto(r.UpdatedAt)
if err != nil {
return nil, err
}
return protobuf.Credential{
Id: r.ID,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
Primary: r.Primary,
SourceHost: r.SourceHost,
LoginUrl: r.LoginURL,
Tag: r.Tag,
Username: r.Username,
Email: r.Email,
Password: r.Password,
OtpSecret: r.OTPSecret,
}, 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,
OTPSecret: r.OtpSecret,
}, nil
}
func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(protobuf.UpdateRequest)
return endpoints.UpdateRequest{
ID: r.Id,
Credential: types.CredentialInput{
MetadataInput: types.MetadataInput{
Primary: r.Credential.Primary,
SourceHost: r.Credential.SourceHost,
LoginURL: r.Credential.LoginUrl,
Tag: r.Credential.Tag,
},
Username: r.Credential.Username,
Email: r.Credential.Email,
Password: r.Credential.Password,
OTPSecret: r.Credential.OtpSecret,
},
}, nil
}
func EncodeUpdateRequest(r endpoints.UpdateRequest) protobuf.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,
OtpSecret: c.OTPSecret,
SourceHost: c.SourceHost,
LoginUrl: c.LoginURL,
Tag: c.Tag,
},
}
}
func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(protobuf.IdRequest)
return endpoints.IDRequest{
ID: r.Id,
}, nil
}
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

@ -0,0 +1,170 @@
package transport
import (
"context"
"strings"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/mitchell/selfpass/services/credentials/endpoints"
"github.com/mitchell/selfpass/services/credentials/protobuf"
"github.com/mitchell/selfpass/services/credentials/types"
)
func NewGRPCServer(svc types.Service, logger log.Logger) GRPCServer {
return GRPCServer{
getAllMetadata: grpc.NewServer(
endpoints.MakeGetAllMetadataEndpoint(svc),
decodeGetAllMetadataRequest,
encodeMetadataStreamResponse,
grpc.ServerErrorLogger(logger),
),
get: grpc.NewServer(
endpoints.MakeGetEndpoint(svc),
decodeIdRequest,
encodeCredentialResponse,
grpc.ServerErrorLogger(logger),
),
create: grpc.NewServer(
endpoints.MakeCreateEndpoint(svc),
decodeCredentialRequest,
encodeCredentialResponse,
grpc.ServerErrorLogger(logger),
),
update: grpc.NewServer(
endpoints.MakeUpdateEndpoint(svc),
decodeUpdateRequest,
encodeCredentialResponse,
grpc.ServerErrorLogger(logger),
),
delete: grpc.NewServer(
endpoints.MakeDeleteEndpoint(svc),
decodeIdRequest,
noOp,
grpc.ServerErrorLogger(logger),
),
dump: grpc.NewServer(
endpoints.MakeDumpEndpoint(svc),
noOp,
encodeDumpResponse,
grpc.ServerErrorLogger(logger),
),
}
}
type GRPCServer struct {
getAllMetadata *grpc.Server
get *grpc.Server
create *grpc.Server
update *grpc.Server
delete *grpc.Server
dump *grpc.Server
}
func (s GRPCServer) GetAllMetadata(r *protobuf.GetAllMetadataRequest, srv protobuf.CredentialService_GetAllMetadataServer) (err error) {
defer func() { err = handlerGRPCError(err) }()
var i interface{}
ctx := srv.Context()
ctx, i, err = s.getAllMetadata.ServeGRPC(ctx, *r)
if err != nil {
return err
}
mds := i.(ProtobufMetadataStream)
receiveLoop:
for {
select {
case <-ctx.Done():
break receiveLoop
case err = <-mds.Errors:
break receiveLoop
case md, ok := <-mds.Metadata:
if !ok {
break receiveLoop
}
if err = srv.Send(&md); err != nil {
break receiveLoop
}
}
}
return err
}
func (s GRPCServer) Get(ctx context.Context, r *protobuf.IdRequest) (*protobuf.Credential, error) {
ctx, i, err := s.get.ServeGRPC(ctx, *r)
if err != nil {
err = handlerGRPCError(err)
return nil, err
}
c := &protobuf.Credential{}
*c = i.(protobuf.Credential)
return c, nil
}
func (s GRPCServer) Create(ctx context.Context, r *protobuf.CredentialRequest) (*protobuf.Credential, error) {
ctx, i, err := s.create.ServeGRPC(ctx, *r)
if err != nil {
err = handlerGRPCError(err)
return nil, err
}
c := &protobuf.Credential{}
*c = i.(protobuf.Credential)
return c, nil
}
func (s GRPCServer) Update(ctx context.Context, r *protobuf.UpdateRequest) (*protobuf.Credential, error) {
ctx, i, err := s.update.ServeGRPC(ctx, *r)
if err != nil {
err = handlerGRPCError(err)
return nil, err
}
c := &protobuf.Credential{}
*c = i.(protobuf.Credential)
return c, nil
}
func (s GRPCServer) Delete(ctx context.Context, r *protobuf.IdRequest) (*protobuf.DeleteResponse, error) {
ctx, _, err := s.delete.ServeGRPC(ctx, *r)
if err != nil {
err = handlerGRPCError(err)
return nil, err
}
return &protobuf.DeleteResponse{Success: true}, nil
}
func (s GRPCServer) Dump(ctx context.Context, r *protobuf.EmptyRequest) (*protobuf.DumpResponse, error) {
ctx, i, err := s.dump.ServeGRPC(ctx, *r)
if err != nil {
err = handlerGRPCError(err)
return nil, err
}
res := &protobuf.DumpResponse{}
*res = i.(protobuf.DumpResponse)
return res, nil
}
func handlerGRPCError(err error) error {
if err != nil {
switch {
case strings.HasPrefix(err.Error(), types.InvalidArgument):
err = status.Error(codes.InvalidArgument, err.Error())
case strings.HasPrefix(err.Error(), types.NotFound):
err = status.Error(codes.NotFound, err.Error())
default:
err = status.Error(codes.Internal, "an internal error has occurred")
}
}
return err
}

View file

@ -0,0 +1,75 @@
package types
import (
"fmt"
"time"
)
const TypePrefixCred = "cred"
type Credential struct {
Metadata
Username string
Email string
Password string `json:"-"`
OTPSecret string `json:"-"`
}
func (c Credential) String() string {
format := "%s"
args := []interface{}{c.Metadata}
if c.Username != "" {
format += "username = %s\n"
args = append(args, c.Username)
}
if c.Email != "" {
format += "email = %s\n"
args = append(args, c.Email)
}
return fmt.Sprintf(format, args...)
}
type CredentialInput struct {
MetadataInput
Username string
Email string
Password string
OTPSecret string
}
type Metadata struct {
ID string // primary key
SourceHost string // sort key
CreatedAt time.Time
UpdatedAt time.Time
Primary string
LoginURL string
Tag string
}
func (m Metadata) String() string {
format := "id = %s\ncreatedAt = %s\nupdatedAt = %s\nsourceHost = %s\nprimary = %s\n"
args := []interface{}{m.ID, m.CreatedAt, m.UpdatedAt, m.SourceHost, m.Primary}
if m.LoginURL != "" {
format += "loginUrl = %s\n"
args = append(args, m.LoginURL)
}
if m.Tag != "" {
format += "tag = %s\n"
args = append(args, m.Tag)
}
return fmt.Sprintf(format, args...)
}
type MetadataInput struct {
Primary string
SourceHost string
LoginURL string
Tag string
}

View file

@ -0,0 +1,8 @@
package types
// These constants are the prefixes of errors that should be exposed to the client.
// All error encoders should check for them.
const (
NotFound = "not found:"
InvalidArgument = "invalid argument:"
)

View file

@ -0,0 +1,30 @@
package types
import "context"
type Service 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)
DumpDB(ctx context.Context) (bs []byte, err error)
}
type CredentialRepo interface {
GetAllMetadata(ctx context.Context, sourceHost string, errch chan<- error) (output <-chan Metadata)
Get(ctx context.Context, id string) (output Credential, err error)
Put(ctx context.Context, c Credential) (err error)
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)
}

68
services/crypto/cbc.go Normal file
View file

@ -0,0 +1,68 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
"github.com/ncw/rclone/backend/crypt/pkcs7"
)
func CBCEncrypt(key []byte, plaintext []byte) ([]byte, error) {
if len(key) != 32 {
return nil, fmt.Errorf("key is not 32 bytes")
}
plaintext = pkcs7.Pad(aes.BlockSize, 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 pkcs7.Unpad(aes.BlockSize, ciphertext)
}

60
services/crypto/gcm.go Normal file
View file

@ -0,0 +1,60 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
"io"
)
func GCMEncrypt(key []byte, plaintext []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
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
func GCMDecrypt(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
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(ciphertext) < gcm.NonceSize() {
return nil, errors.New("malformed ciphertext")
}
return gcm.Open(nil,
ciphertext[:gcm.NonceSize()],
ciphertext[gcm.NonceSize():],
nil,
)
}

View 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)
}

16
services/crypto/pbkdf2.go Normal file
View file

@ -0,0 +1,16 @@
package crypto
import (
"crypto/sha256"
"golang.org/x/crypto/pbkdf2"
)
const (
PBKDF2Rounds = 4096
KeyLength = 32
)
func GeneratePBKDF2Key(password, salt []byte) []byte {
return pbkdf2.Key([]byte(password), []byte(salt), PBKDF2Rounds, KeyLength, sha256.New)
}

View file

@ -0,0 +1,9 @@
version: "3.7"
services:
redis:
volumes:
- "/home/selfpass/data:/data"
- "/home/selfpass/redis.conf:/redis.conf"
server:
entrypoint:
- server

View file

@ -0,0 +1,21 @@
version: "3.7"
services:
redis:
image: "redis:5.0.5"
restart: on-failure
command:
- redis-server
- /redis.conf
volumes:
- "./redis.conf:/redis.conf"
- "./data:/data"
server:
build: .
restart: on-failure
entrypoint:
- server
- -v
ports:
- "8080:8080"
depends_on:
- redis

13
services/gen_certs_go.sh Executable file
View 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

29
services/go.mod Normal file
View file

@ -0,0 +1,29 @@
module github.com/mitchell/selfpass/services
go 1.12
require (
github.com/atotto/clipboard v0.1.2
github.com/boombuler/barcode v1.0.0 // indirect
github.com/go-kit/kit v0.8.0
github.com/golang/protobuf v1.3.1
github.com/google/uuid v1.1.1
github.com/kr/pty v1.1.5 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mediocregopher/radix/v3 v3.2.3
github.com/mitchellh/go-homedir v1.1.0
github.com/ncw/rclone v1.48.0
github.com/pelletier/go-toml v1.4.0 // indirect
github.com/pquerna/otp v1.1.0
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.5
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.4.0
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 // indirect
golang.org/x/sys v0.0.0-20190614160838-b47fdc937951 // indirect
google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 // indirect
google.golang.org/grpc v1.21.1
gopkg.in/AlecAivazis/survey.v1 v1.8.5
)

353
services/go.sum Normal file
View file

@ -0,0 +1,353 @@
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/Unknwon/goconfig v0.0.0-20181105214110-56bd8ab18619/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
github.com/a8m/tree v0.0.0-20181222104329-6a0b80129de4/go.mod h1:FSdwKX97koS5efgm8WevNf7XS3PqtyFkKDDXrz778cg=
github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anacrolix/dms v0.0.0-20180117034613-8af4925bffb5/go.mod h1:DGqLjaZ3ziKKNRt+U5Q9PLWJ52Q/4rxfaaH/b3QYKaE=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
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 v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/billziss-gh/cgofuse v1.1.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nCR84CyxKt2YM=
github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
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/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/djherbis/times v1.2.0/go.mod h1:CGMZlo255K5r4Yw0b9RRfFQpM2y7uOmxg4jm9HsaVf8=
github.com/dropbox/dropbox-sdk-go-unofficial v5.4.0+incompatible/go.mod h1:lr+LhMM3F6Y3lW1T9j2U5l7QeuWm87N9+PPXo3yH4qY=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
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/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss=
github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
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/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jlaffaye/ftp v0.0.0-20190519203911-8f5b34ce006f/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koofr/go-httpclient v0.0.0-20180104120329-03786175608a/go.mod h1:3xszwh+rNrYk1r9SStc4iJ326gne1OaBcrdB1ACsbzI=
github.com/koofr/go-koofrclient v0.0.0-20190131164641-7f327592caff/go.mod h1:MRAz4Gsxd+OzrZ0owwrUHc0zLESL+1Y5syqK/sJxK2A=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/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.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
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.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncw/go-acd v0.0.0-20171120105400-887eb06ab6a2/go.mod h1:MLIrzg7gp/kzVBxRE1olT7CWYMCklcUWU+ekoxOD9x0=
github.com/ncw/rclone v1.48.0 h1:Rc7A4YEQDeMPgnc1IzA6PsJ4YikyP+zS68rgGMYKJ7o=
github.com/ncw/rclone v1.48.0/go.mod h1:CXDUKN1OQ3Y2ya1Ma6jTZ7m9ZarGzF3ZTHsdPLHWWzY=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd/go.mod h1:4soZNh0zW0LtYGdQ416i0jO0EIqMGcbtaspRS4BDvRQ=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
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/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pengsrc/go-shared v0.2.0/go.mod h1:jVblp62SafmidSkvWrXyxAme3gaTfEtWwRPGz5cpvHg=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1-0.20190523025818-e98a7bef6829/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
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/pquerna/otp v1.1.0 h1:q2gMsMuMl3JzneUaAX1MRGxLvOG6bzXV51hivBaStf0=
github.com/pquerna/otp v1.1.0/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rfjakob/eme v0.0.0-20171028163933-2222dbd4ba46/go.mod h1:U2bmx0hDj8EyDdcxmD5t3XHDnBFnyNNc22n1R4008eM=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sevlyar/go-daemon v0.1.4/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
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.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
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.4-0.20190321000552-67fc4837d267/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
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/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
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/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/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 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
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/t3rm1n4l/go-mega v0.0.0-20190430100803-72151b53bb44/go.mod h1:XWL4vDyd3JKmJx+hZWUVgCNmmhZ2dTBcaNDcxH465s0=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yunify/qingstor-sdk-go v2.2.15+incompatible/go.mod h1:w6wqLDQ5bBTzxGJ55581UrSwLrsTAsdo9N6yX/8d9RY=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/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/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190606173856-1492cefac77f/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190614160838-b47fdc937951 h1:ZUgGZ7PSkne6oY+VgAvayrB16owfm9/DKAtgWubzgzU=
golang.org/x/sys v0.0.0-20190614160838-b47fdc937951/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.1-0.20180807135948-17ff2d5776d2/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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190606174628-0139d5756a7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.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/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3 h1:0LGHEA/u5XLibPOx6D7D8FBT/ax6wT57vNKY0QckCwo=
google.golang.org/genproto v0.0.0-20190611190212-a7e196e89fd3/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/AlecAivazis/survey.v1 v1.8.5 h1:QoEEmn/d5BbuPIL2qvXwzJdttFFhRQFkaq+tEKb7SMI=
gopkg.in/AlecAivazis/survey.v1 v1.8.5/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

1372
services/redis.conf Normal file

File diff suppressed because it is too large Load diff