mirror of
https://github.com/mitchell/selfpass.git
synced 2026-02-12 18:16:51 +00:00
Move service related filed to services folder
This commit is contained in:
parent
347fbe7268
commit
7d770ef150
41 changed files with 50 additions and 50 deletions
13
services/Dockerfile
Normal file
13
services/Dockerfile
Normal 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
94
services/Makefile
Normal 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
52
services/README.md
Normal 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)
|
||||
50
services/cli/commands/decrypt.go
Normal file
50
services/cli/commands/decrypt.go
Normal 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
|
||||
}
|
||||
28
services/cli/commands/decrypt_cfg.go
Normal file
28
services/cli/commands/decrypt_cfg.go
Normal 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
|
||||
}
|
||||
46
services/cli/commands/encrypt.go
Normal file
46
services/cli/commands/encrypt.go
Normal 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
|
||||
}
|
||||
87
services/cli/commands/init.go
Normal file
87
services/cli/commands/init.go
Normal 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
|
||||
}
|
||||
72
services/cli/commands/root.go
Normal file
72
services/cli/commands/root.go
Normal 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)
|
||||
}
|
||||
}
|
||||
159
services/cli/repositories/config.go
Normal file
159
services/cli/repositories/config.go
Normal 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
|
||||
13
services/cli/types/types.go
Normal file
13
services/cli/types/types.go
Normal 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"
|
||||
113
services/cmd/server/server.go
Normal file
113
services/cmd/server/server.go
Normal 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
9
services/cmd/spc/main.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/mitchell/selfpass/services/cli/commands"
|
||||
)
|
||||
|
||||
func main() {
|
||||
commands.Execute()
|
||||
}
|
||||
100
services/credentials/commands/commands.go
Normal file
100
services/credentials/commands/commands.go
Normal 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
|
||||
}
|
||||
155
services/credentials/commands/create.go
Normal file
155
services/credentials/commands/create.go
Normal 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, ©pass, 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, ©otp, nil))
|
||||
|
||||
if copyotp {
|
||||
otp, err := totp.GenerateCode(secret, time.Now())
|
||||
check(err)
|
||||
|
||||
check(clipboard.WriteAll(otp))
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*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
|
||||
}
|
||||
42
services/credentials/commands/delete.go
Normal file
42
services/credentials/commands/delete.go
Normal 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
|
||||
}
|
||||
108
services/credentials/commands/gcm-to-cbc.go
Normal file
108
services/credentials/commands/gcm-to-cbc.go
Normal 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
|
||||
}
|
||||
94
services/credentials/commands/get.go
Normal file
94
services/credentials/commands/get.go
Normal 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, ©Pass, nil))
|
||||
|
||||
if copyPass {
|
||||
passbytes, err := base64.StdEncoding.DecodeString(cred.Password)
|
||||
check(err)
|
||||
|
||||
plainpass, err := crypto.CBCDecrypt(passkey, passbytes)
|
||||
|
||||
check(clipboard.WriteAll(string(plainpass)))
|
||||
|
||||
fmt.Println("Wrote password to clipboard.")
|
||||
}
|
||||
|
||||
if cred.OTPSecret != "" {
|
||||
var newOTP bool
|
||||
prompt = &survey.Confirm{Message: "Generate one time password and copy to clipboard?", Default: true}
|
||||
check(survey.AskOne(prompt, &newOTP, nil))
|
||||
|
||||
if newOTP {
|
||||
secretbytes, err := base64.StdEncoding.DecodeString(cred.OTPSecret)
|
||||
check(err)
|
||||
|
||||
plainsecret, err := crypto.CBCDecrypt(passkey, secretbytes)
|
||||
|
||||
otp, err := totp.GenerateCode(string(plainsecret), time.Now())
|
||||
check(err)
|
||||
|
||||
check(clipboard.WriteAll(otp))
|
||||
|
||||
fmt.Println("Wrote one time password to clipboard.")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
77
services/credentials/commands/list.go
Normal file
77
services/credentials/commands/list.go
Normal 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
|
||||
}
|
||||
190
services/credentials/commands/update.go
Normal file
190
services/credentials/commands/update.go
Normal 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, ©pass, 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, ©otp, nil))
|
||||
|
||||
if copyotp {
|
||||
otp, err := totp.GenerateCode(secret, time.Now())
|
||||
check(err)
|
||||
|
||||
check(clipboard.WriteAll(otp))
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), time.Second*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
|
||||
}
|
||||
78
services/credentials/endpoints/endpoints.go
Normal file
78
services/credentials/endpoints/endpoints.go
Normal 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
|
||||
}
|
||||
114
services/credentials/middleware/logger.go
Normal file
114
services/credentials/middleware/logger.go
Normal 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
|
||||
}
|
||||
867
services/credentials/protobuf/service.pb.go
Normal file
867
services/credentials/protobuf/service.pb.go
Normal 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",
|
||||
}
|
||||
75
services/credentials/protobuf/service.proto
Normal file
75
services/credentials/protobuf/service.proto
Normal 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;
|
||||
}
|
||||
136
services/credentials/repositories/grpc_client.go
Normal file
136
services/credentials/repositories/grpc_client.go
Normal 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
|
||||
}
|
||||
81
services/credentials/repositories/redis.go
Normal file
81
services/credentials/repositories/redis.go
Normal 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"
|
||||
)
|
||||
118
services/credentials/service/service.go
Normal file
118
services/credentials/service/service.go
Normal 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)
|
||||
}
|
||||
254
services/credentials/transport/encoding.go
Normal file
254
services/credentials/transport/encoding.go
Normal 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
|
||||
}
|
||||
170
services/credentials/transport/grpc_server.go
Normal file
170
services/credentials/transport/grpc_server.go
Normal 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
|
||||
}
|
||||
75
services/credentials/types/credential.go
Normal file
75
services/credentials/types/credential.go
Normal 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
|
||||
}
|
||||
8
services/credentials/types/error_prefixes.go
Normal file
8
services/credentials/types/error_prefixes.go
Normal 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:"
|
||||
)
|
||||
30
services/credentials/types/interfaces.go
Normal file
30
services/credentials/types/interfaces.go
Normal 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
68
services/crypto/cbc.go
Normal 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
60
services/crypto/gcm.go
Normal 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,
|
||||
)
|
||||
}
|
||||
68
services/crypto/helpers.go
Normal file
68
services/crypto/helpers.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateKeyFromPassword(pass []byte) ([]byte, error) {
|
||||
if len(pass) < 8 {
|
||||
return nil, fmt.Errorf("master password must be at least 8 characters")
|
||||
}
|
||||
|
||||
for idx := 0; len(pass) < 32; idx++ {
|
||||
pass = append(pass, pass[idx])
|
||||
|
||||
if idx == len(pass) {
|
||||
idx = 0
|
||||
}
|
||||
}
|
||||
|
||||
return pass, nil
|
||||
}
|
||||
|
||||
func CombinePasswordAndKey(pass, key []byte) ([]byte, error) {
|
||||
if len(pass) < 8 {
|
||||
return nil, fmt.Errorf("master password must be at least 8 characters")
|
||||
}
|
||||
if len(key) != 16 {
|
||||
return nil, fmt.Errorf("key was not of length 16")
|
||||
}
|
||||
|
||||
for idx := 0; len(pass) < 16; idx++ {
|
||||
pass = append(pass, pass[idx])
|
||||
}
|
||||
|
||||
return append(pass[:16], key...), nil
|
||||
}
|
||||
|
||||
func GeneratePassword(length int, numbers, specials bool) string {
|
||||
const alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
const alphanumerics = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||
const alphasAndSpecials = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()"
|
||||
const alphanumericsAndSpecials = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()"
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
pass := make([]byte, length)
|
||||
|
||||
switch {
|
||||
case numbers && specials:
|
||||
for idx := 0; idx < length; idx++ {
|
||||
pass[idx] = alphanumericsAndSpecials[rand.Int63()%int64(len(alphanumericsAndSpecials))]
|
||||
}
|
||||
case numbers:
|
||||
for idx := 0; idx < length; idx++ {
|
||||
pass[idx] = alphanumerics[rand.Int63()%int64(len(alphanumerics))]
|
||||
}
|
||||
case specials:
|
||||
for idx := 0; idx < length; idx++ {
|
||||
pass[idx] = alphasAndSpecials[rand.Int63()%int64(len(alphasAndSpecials))]
|
||||
}
|
||||
default:
|
||||
for idx := 0; idx < length; idx++ {
|
||||
pass[idx] = alphas[rand.Int63()%int64(len(alphas))]
|
||||
}
|
||||
}
|
||||
|
||||
return string(pass)
|
||||
}
|
||||
16
services/crypto/pbkdf2.go
Normal file
16
services/crypto/pbkdf2.go
Normal 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)
|
||||
}
|
||||
9
services/docker-compose.prod.yml
Normal file
9
services/docker-compose.prod.yml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
version: "3.7"
|
||||
services:
|
||||
redis:
|
||||
volumes:
|
||||
- "/home/selfpass/data:/data"
|
||||
- "/home/selfpass/redis.conf:/redis.conf"
|
||||
server:
|
||||
entrypoint:
|
||||
- server
|
||||
21
services/docker-compose.yml
Normal file
21
services/docker-compose.yml
Normal 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
13
services/gen_certs_go.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env sh
|
||||
ca=$(cat ./certs/ca.pem)
|
||||
cert=$(cat ./certs/server.pem)
|
||||
key=$(cat ./certs/server-key.pem)
|
||||
|
||||
cat << EOM
|
||||
// Code generated by inject-certs.sh, DO NOT EDIT.
|
||||
package main
|
||||
|
||||
const ca = \`${ca}\`
|
||||
const cert = \`${cert}\`
|
||||
const key = \`${key}\`
|
||||
EOM
|
||||
29
services/go.mod
Normal file
29
services/go.mod
Normal 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
353
services/go.sum
Normal 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
1372
services/redis.conf
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue