mirror of https://github.com/mitchell/selfpass.git
Implemented encryption functionality of spc and password generation; refactors on spc and server
This commit is contained in:
parent
c289eecd54
commit
cd24f6e848
15
Dockerfile
15
Dockerfile
|
@ -1,22 +1,11 @@
|
||||||
FROM golang:1.11.5 as build
|
FROM golang:1.11.5 as build
|
||||||
WORKDIR /go/src/github.com/mitchell/selfpass
|
WORKDIR /go/src/github.com/mitchell/selfpass
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN go get -u golang.org/x/tools/cmd/goimports
|
|
||||||
ENV GO111MODULE=on
|
ENV GO111MODULE=on
|
||||||
RUN make build
|
RUN make build
|
||||||
|
|
||||||
FROM debian:stable-20190326-slim
|
FROM debian:stable-20190326-slim
|
||||||
RUN printf "deb http://httpredir.debian.org/debian stretch-backports main non-free\ndeb-src http://httpredir.debian.org/debian stretch-backports main non-free" > /etc/apt/sources.list.d/backports.list
|
WORKDIR /usr/bin
|
||||||
RUN apt-get update && apt-get install -t stretch-backports -y --no-install-recommends redis-server=5:5.0.3-3~bpo9+2 \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
WORKDIR /usr/bin/selfpass/
|
|
||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/bin/server .
|
COPY --from=build /go/src/github.com/mitchell/selfpass/bin/server .
|
||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/redis.conf .
|
ENTRYPOINT ["server"]
|
||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/db/dump.rdb ./db/dump.rdb
|
|
||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/certs/ca.pem ./certs/ca.pem
|
|
||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/certs/server.pem ./certs/server.pem
|
|
||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/certs/server-key.pem ./certs/server-key.pem
|
|
||||||
COPY --from=build /go/src/github.com/mitchell/selfpass/dual-entry ./dual-entry
|
|
||||||
ENTRYPOINT ./dual-entry
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
18
Makefile
18
Makefile
|
@ -1,23 +1,23 @@
|
||||||
.PHONY: all build clean format test docker-build
|
.PHONY: all build clean format test docker-build
|
||||||
|
|
||||||
build: clean format test
|
build: clean format test
|
||||||
go build -o ./bin/server ./cmd/server/server.go
|
go build --o ./bin/server ./cmd/server/server.go
|
||||||
|
|
||||||
docker-build:
|
|
||||||
docker build -t selfpass .
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf ./bin
|
rm -rf ./bin
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
dev: docker-build
|
docker:
|
||||||
docker run -i -t -p 8080:8080 selfpass -v -dev
|
docker-compose build
|
||||||
|
|
||||||
local: docker-build
|
start:
|
||||||
docker run -i -t -p 8080:8080 selfpass
|
docker-compose up
|
||||||
|
|
||||||
format:
|
format:
|
||||||
goimports -w -l .
|
gofmt -w -s -l .
|
||||||
|
|
||||||
|
install-spc:
|
||||||
|
go install ./cmd/spc
|
||||||
|
|
||||||
gen-protoc:
|
gen-protoc:
|
||||||
protoc --go_out=plugins=grpc:. \
|
protoc --go_out=plugins=grpc:. \
|
||||||
|
|
|
@ -10,16 +10,18 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/go-kit/kit/log"
|
"github.com/go-kit/kit/log"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
|
||||||
"github.com/mitchell/selfpass/credentials/middleware"
|
"github.com/mitchell/selfpass/credentials/middleware"
|
||||||
"github.com/mitchell/selfpass/credentials/protobuf"
|
"github.com/mitchell/selfpass/credentials/protobuf"
|
||||||
"github.com/mitchell/selfpass/credentials/repositories"
|
"github.com/mitchell/selfpass/credentials/repositories"
|
||||||
"github.com/mitchell/selfpass/credentials/service"
|
"github.com/mitchell/selfpass/credentials/service"
|
||||||
"github.com/mitchell/selfpass/credentials/transport"
|
"github.com/mitchell/selfpass/credentials/transport"
|
||||||
"github.com/mitchell/selfpass/credentials/types"
|
"github.com/mitchell/selfpass/credentials/types"
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger log.Logger
|
var logger log.Logger
|
||||||
|
@ -29,9 +31,9 @@ func main() {
|
||||||
stop = make(chan os.Signal, 1)
|
stop = make(chan os.Signal, 1)
|
||||||
dev = flag.Bool("dev", false, "enables dev mode logging")
|
dev = flag.Bool("dev", false, "enables dev mode logging")
|
||||||
port = flag.String("port", "8080", "specify the port to listen on")
|
port = flag.String("port", "8080", "specify the port to listen on")
|
||||||
crtFile = flag.String("cert", "./certs/server.pem", "specify the cert file")
|
crtFile = flag.String("cert", "/run/secrets/server_cert", "specify the cert file")
|
||||||
keyFile = flag.String("key", "./certs/server-key.pem", "specify the private key file")
|
keyFile = flag.String("key", "/run/secrets/server_key", "specify the private key file")
|
||||||
caFile = flag.String("ca", "./certs/ca.pem", "specify the ca cert file")
|
caFile = flag.String("ca", "/run/secrets/ca_cert", "specify the ca cert file")
|
||||||
verbose = flag.Bool("v", false, "be more verbose")
|
verbose = flag.Bool("v", false, "be more verbose")
|
||||||
// tableName = flag.String(
|
// tableName = flag.String(
|
||||||
// "credential-table-name",
|
// "credential-table-name",
|
||||||
|
@ -39,10 +41,12 @@ func main() {
|
||||||
// "specify the credential table name on AWS",
|
// "specify the credential table name on AWS",
|
||||||
// )
|
// )
|
||||||
)
|
)
|
||||||
|
|
||||||
signal.Notify(stop, os.Interrupt)
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
signal.Notify(stop, syscall.SIGINT)
|
||||||
|
signal.Notify(stop, syscall.SIGKILL)
|
||||||
|
signal.Notify(stop, syscall.SIGTERM)
|
||||||
|
|
||||||
logger = newLogger(os.Stdout, *dev)
|
logger = newLogger(os.Stdout, *dev)
|
||||||
|
|
||||||
keypair, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
|
keypair, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
|
||||||
|
@ -61,9 +65,7 @@ func main() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// db := repositories.NewDynamoTable(*tableName)
|
// db := repositories.NewDynamoTable(*tableName)
|
||||||
db, err := repositories.NewRedisConn(
|
db, err := repositories.NewRedisConn("tcp", "redis:6379", 2)
|
||||||
repositories.ConnConfig{NetworkType: "tcp", Address: "localhost:6379", Size: 2},
|
|
||||||
)
|
|
||||||
check(err)
|
check(err)
|
||||||
|
|
||||||
var svc types.Service
|
var svc types.Service
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"gopkg.in/AlecAivazis/survey.v1"
|
||||||
|
|
||||||
|
"github.com/mitchell/selfpass/credentials/cmds"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeInitCmd(cfg *viper.Viper) *cobra.Command {
|
||||||
|
initCmd := &cobra.Command{
|
||||||
|
Use: "init",
|
||||||
|
Short: "This command initializes SPC for the first time",
|
||||||
|
Long: `This command initializes SPC for the first time. Writing to the user configuration
|
||||||
|
the users private key, and server certificates. (All of which will be encrypted)`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
var (
|
||||||
|
hasPK bool
|
||||||
|
masterpass string
|
||||||
|
cmasterpass string
|
||||||
|
target string
|
||||||
|
caFile string
|
||||||
|
certFile string
|
||||||
|
keyFile string
|
||||||
|
prompt survey.Prompt
|
||||||
|
privateKey = strings.Replace(uuid.New().String(), "-", "", -1)
|
||||||
|
)
|
||||||
|
|
||||||
|
prompt = &survey.Password{Message: "Master password:"}
|
||||||
|
check(survey.AskOne(prompt, &masterpass, nil))
|
||||||
|
|
||||||
|
prompt = &survey.Password{Message: "Confirm master password:"}
|
||||||
|
check(survey.AskOne(prompt, &cmasterpass, nil))
|
||||||
|
if masterpass != cmasterpass {
|
||||||
|
check(fmt.Errorf("master passwords didn't match"))
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt = &survey.Input{Message: "Selfpass server address:"}
|
||||||
|
check(survey.AskOne(prompt, &target, nil))
|
||||||
|
|
||||||
|
prompt = &survey.Confirm{Message: "Do you have a private key?"}
|
||||||
|
check(survey.AskOne(prompt, &hasPK, nil))
|
||||||
|
|
||||||
|
if hasPK {
|
||||||
|
prompt = &survey.Input{Message: "Private key:"}
|
||||||
|
check(survey.AskOne(prompt, &privateKey, nil))
|
||||||
|
privateKey = strings.Replace(privateKey, "-", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt = &survey.Input{Message: "CA certificate file:"}
|
||||||
|
check(survey.AskOne(prompt, &caFile, nil))
|
||||||
|
ca, err := ioutil.ReadFile(caFile)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
prompt = &survey.Input{Message: "Client certificate file:"}
|
||||||
|
check(survey.AskOne(prompt, &certFile, nil))
|
||||||
|
cert, err := ioutil.ReadFile(certFile)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
prompt = &survey.Input{Message: "Client key file:"}
|
||||||
|
check(survey.AskOne(prompt, &keyFile, nil))
|
||||||
|
key, err := ioutil.ReadFile(keyFile)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
cfg.Set(cmds.KeyConnConfig, map[string]string{
|
||||||
|
"target": target,
|
||||||
|
"ca": string(ca),
|
||||||
|
"cert": string(cert),
|
||||||
|
"key": string(key),
|
||||||
|
})
|
||||||
|
|
||||||
|
cfg.Set(cmds.KeyPrivateKey, privateKey)
|
||||||
|
|
||||||
|
if err := cfg.WriteConfig(); err != nil {
|
||||||
|
home, err := homedir.Dir()
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
check(cfg.WriteConfigAs(home + "/.spc.toml"))
|
||||||
|
cfg.SetConfigFile(home + "/.spc.toml")
|
||||||
|
fmt.Println("Wrote new config to: " + home + "/.spc.toml")
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptConfig(masterpass, cfg)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return initCmd
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"gopkg.in/AlecAivazis/survey.v1"
|
||||||
|
|
||||||
|
"github.com/mitchell/selfpass/credentials/cmds"
|
||||||
|
"github.com/mitchell/selfpass/credentials/types"
|
||||||
|
"github.com/mitchell/selfpass/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Execute(ctx context.Context, initClient types.CredentialClientInit) {
|
||||||
|
rootCmd := &cobra.Command{
|
||||||
|
Use: "spc",
|
||||||
|
Short: "This is the CLI client for Selfpass.",
|
||||||
|
Long: `This is the CLI client for Selfpass, the self-hosted password manager. With this tool you
|
||||||
|
can interact with the entire Selfpass API.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgFile := rootCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.spc.toml)")
|
||||||
|
rootCmd.PersistentFlags().Parse(os.Args)
|
||||||
|
|
||||||
|
decryptCfg := rootCmd.Flags().Bool("decrypt-cfg", false, "unencrypt config file")
|
||||||
|
rootCmd.Flags().Parse(os.Args)
|
||||||
|
|
||||||
|
encryptCfg := !*decryptCfg
|
||||||
|
masterpass, cfg := openConfig(*cfgFile)
|
||||||
|
if encryptCfg && masterpass != "" {
|
||||||
|
defer encryptConfig(masterpass, cfg)
|
||||||
|
}
|
||||||
|
if *decryptCfg {
|
||||||
|
fmt.Println("Decrypting config file. It will auto-encrypt when you next run of spc.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.AddCommand(makeInitCmd(cfg))
|
||||||
|
rootCmd.AddCommand(cmds.MakeListCmd(makeInitClient(cfg, initClient)))
|
||||||
|
rootCmd.AddCommand(cmds.MakeCreateCmd(masterpass, cfg, makeInitClient(cfg, initClient)))
|
||||||
|
rootCmd.AddCommand(cmds.MakeGetCmd(masterpass, cfg, makeInitClient(cfg, initClient)))
|
||||||
|
|
||||||
|
check(rootCmd.Execute())
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInitClient(cfg *viper.Viper, initClient types.CredentialClientInit) cmds.CredentialClientInit {
|
||||||
|
return func(ctx context.Context) types.CredentialClient {
|
||||||
|
connConfig := cfg.GetStringMapString(cmds.KeyConnConfig)
|
||||||
|
|
||||||
|
client, err := initClient(
|
||||||
|
ctx,
|
||||||
|
connConfig["target"],
|
||||||
|
connConfig["ca"],
|
||||||
|
connConfig["cert"],
|
||||||
|
connConfig["key"],
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Please run 'init' command before running API commands.\nError Message: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func openConfig(cfgFile string) (masterpass string, v *viper.Viper) {
|
||||||
|
v = viper.New()
|
||||||
|
v.SetConfigType("toml")
|
||||||
|
|
||||||
|
if cfgFile != "" {
|
||||||
|
// Use config file from the flag.
|
||||||
|
v.SetConfigFile(cfgFile)
|
||||||
|
} else {
|
||||||
|
// Find home directory.
|
||||||
|
home, err := homedir.Dir()
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
// Search config in home directory with name ".spc" (without extension).
|
||||||
|
v.AddConfigPath(home)
|
||||||
|
v.SetConfigName(".spc")
|
||||||
|
|
||||||
|
cfgFile = home + "/.spc.toml"
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Open(cfgFile); !os.IsNotExist(err) {
|
||||||
|
prompt := &survey.Password{Message: "Master password:"}
|
||||||
|
check(survey.AskOne(prompt, &masterpass, nil))
|
||||||
|
|
||||||
|
decryptConfig(masterpass, cfgFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
//v.AutomaticEnv() // read in environment variables that match
|
||||||
|
|
||||||
|
// If a config file is found, read it in.
|
||||||
|
if err := v.ReadInConfig(); err == nil {
|
||||||
|
fmt.Println("Using config file:", v.ConfigFileUsed())
|
||||||
|
}
|
||||||
|
|
||||||
|
return masterpass, v
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptConfig(masterpass string, cfgFile string) {
|
||||||
|
contents, err := ioutil.ReadFile(cfgFile)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
passkey, err := crypto.GenerateKeyFromPassword([]byte(masterpass))
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
contents, err = crypto.CBCDecrypt(passkey, contents)
|
||||||
|
if err != nil && err.Error() == "Padding incorrect" {
|
||||||
|
fmt.Println("incorrect master password")
|
||||||
|
os.Exit(1)
|
||||||
|
} else if err != nil && err.Error() == "ciphertext is not a multiple of the block size" {
|
||||||
|
fmt.Println("Config wasn't encrypted.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
check(ioutil.WriteFile(cfgFile, contents, 0600))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptConfig(masterpass string, cfg *viper.Viper) {
|
||||||
|
contents, err := ioutil.ReadFile(cfg.ConfigFileUsed())
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keypass, err := crypto.GenerateKeyFromPassword([]byte(masterpass))
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
contents, err = crypto.CBCEncrypt(keypass, contents)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
check(ioutil.WriteFile(cfg.ConfigFileUsed(), contents, 0600))
|
||||||
|
}
|
||||||
|
|
||||||
|
func check(err error) {
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/mitchell/selfpass/cmd/spc/cmd"
|
||||||
|
"github.com/mitchell/selfpass/credentials/repositories"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
cmd.Execute(ctx, repositories.NewCredentialServiceClient)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package cmds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/mitchell/selfpass/credentials/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CredentialClientInit func(ctx context.Context) (c types.CredentialClient)
|
||||||
|
|
||||||
|
func check(err error) {
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const KeyConnConfig = "connection"
|
||||||
|
const KeyPrivateKey = "private_key"
|
|
@ -0,0 +1,146 @@
|
||||||
|
package cmds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/atotto/clipboard"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"gopkg.in/AlecAivazis/survey.v1"
|
||||||
|
|
||||||
|
"github.com/mitchell/selfpass/credentials/types"
|
||||||
|
"github.com/mitchell/selfpass/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeCreateCmd(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
|
||||||
|
createCmd := &cobra.Command{
|
||||||
|
Use: "create",
|
||||||
|
Short: "Create a credential in Selfpass",
|
||||||
|
Long: `Create a credential in Selfpass, and save it to the server after encrypting the
|
||||||
|
password.`,
|
||||||
|
|
||||||
|
Run: func(_ *cobra.Command, args []string) {
|
||||||
|
mdqs := []*survey.Question{
|
||||||
|
{
|
||||||
|
Name: "primary",
|
||||||
|
Prompt: &survey.Input{Message: "Primary user key:"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "sourceHost",
|
||||||
|
Prompt: &survey.Input{Message: "Source host:"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "loginURL",
|
||||||
|
Prompt: &survey.Input{Message: "Login url:"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "tag",
|
||||||
|
Prompt: &survey.Input{Message: "Tag:"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cqs := []*survey.Question{
|
||||||
|
{
|
||||||
|
Name: "username",
|
||||||
|
Prompt: &survey.Input{Message: "Username:"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "email",
|
||||||
|
Prompt: &survey.Input{Message: "Email:"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var ci types.CredentialInput
|
||||||
|
|
||||||
|
check(survey.Ask(mdqs, &ci.MetadataInput))
|
||||||
|
check(survey.Ask(cqs, &ci))
|
||||||
|
|
||||||
|
var newpass bool
|
||||||
|
prompt := &survey.Confirm{Message: "Do you want a random password?", Default: true}
|
||||||
|
check(survey.AskOne(prompt, &newpass, nil))
|
||||||
|
|
||||||
|
if newpass {
|
||||||
|
ci.Password = generatePassword(16, true, true)
|
||||||
|
|
||||||
|
var copypass bool
|
||||||
|
prompt = &survey.Confirm{Message: "Copy new pass to clipboard?", Default: true}
|
||||||
|
check(survey.AskOne(prompt, ©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 assword: "}
|
||||||
|
check(survey.AskOne(prompt, &cpass, nil))
|
||||||
|
|
||||||
|
if ci.Password != cpass {
|
||||||
|
fmt.Println("passwords didn't match'")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
keypass, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
cipherpass, err := crypto.CBCEncrypt(keypass, []byte(ci.Password))
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
ci.Password = base64.StdEncoding.EncodeToString(cipherpass)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c, err := initClient(ctx).Create(ctx, ci)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
mdjson, err := json.MarshalIndent(c.Metadata, "", " ")
|
||||||
|
check(err)
|
||||||
|
fmt.Println(string(mdjson))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return createCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
const alphas = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV"
|
||||||
|
const alphanumerics = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV1234567890"
|
||||||
|
const alphasAndSpecials = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV1234567890!@#$%^&*()"
|
||||||
|
const alphanumericsAndSpecials = "abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUV1234567890!@#$%^&*()"
|
||||||
|
|
||||||
|
func generatePassword(length int, numbers, specials bool) string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
pass := make([]byte, length)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case numbers && specials:
|
||||||
|
for idx := 0; idx < length; idx++ {
|
||||||
|
pass[idx] = alphanumericsAndSpecials[rand.Int63()%int64(len(alphanumericsAndSpecials))]
|
||||||
|
}
|
||||||
|
case numbers:
|
||||||
|
for idx := 0; idx < length; idx++ {
|
||||||
|
pass[idx] = alphanumerics[rand.Int63()%int64(len(alphanumerics))]
|
||||||
|
}
|
||||||
|
case specials:
|
||||||
|
for idx := 0; idx < length; idx++ {
|
||||||
|
pass[idx] = alphasAndSpecials[rand.Int63()%int64(len(alphasAndSpecials))]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for idx := 0; idx < length; idx++ {
|
||||||
|
pass[idx] = alphas[rand.Int63()%int64(len(alphas))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pass)
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package cmds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/atotto/clipboard"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/mitchell/selfpass/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeGetCmd(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
|
||||||
|
getCmd := &cobra.Command{
|
||||||
|
Use: "get [id]",
|
||||||
|
Short: "Get a credential info and copy password to clipboard",
|
||||||
|
Long: `Get a credential's info and copy password to clipboard, from Selfpass server, after
|
||||||
|
decrypting password.`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cbcontents, err := clipboard.ReadAll()
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
restore := func(cbcontents string) {
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
clipboard.WriteAll(cbcontents)
|
||||||
|
}
|
||||||
|
|
||||||
|
cred, err := initClient(ctx).Get(ctx, args[0])
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), key)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
passbytes, err := base64.StdEncoding.DecodeString(cred.Password)
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
plainpass, err := crypto.CBCDecrypt(passkey, passbytes)
|
||||||
|
|
||||||
|
check(clipboard.WriteAll(string(plainpass)))
|
||||||
|
go restore(cbcontents)
|
||||||
|
|
||||||
|
cjson, err := json.MarshalIndent(cred, "", " ")
|
||||||
|
check(err)
|
||||||
|
fmt.Println(string(cjson))
|
||||||
|
fmt.Println("Wrote password to clipboard.")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCmd
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package cmds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeListCmd(initClient CredentialClientInit) *cobra.Command {
|
||||||
|
var sourceHost string
|
||||||
|
|
||||||
|
listCmd := &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List the metadata for all credentials",
|
||||||
|
Long: `List the metadata for all credentials, with the option to filter by source host. Metadata
|
||||||
|
includes almost all the information but the most sensitive.`,
|
||||||
|
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mdch, errch := initClient(ctx).GetAllMetadata(ctx, sourceHost)
|
||||||
|
|
||||||
|
receive:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
check(fmt.Errorf("context timeout"))
|
||||||
|
|
||||||
|
case err := <-errch:
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
case md, ok := <-mdch:
|
||||||
|
if !ok {
|
||||||
|
break receive
|
||||||
|
}
|
||||||
|
|
||||||
|
mdjson, err := json.MarshalIndent(md, "", " ")
|
||||||
|
check(err)
|
||||||
|
fmt.Println(string(mdjson))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
listCmd.Flags().StringVarP(
|
||||||
|
&sourceHost,
|
||||||
|
"source-host",
|
||||||
|
"s",
|
||||||
|
"",
|
||||||
|
"specify which source host to filter the results by",
|
||||||
|
)
|
||||||
|
|
||||||
|
return listCmd
|
||||||
|
}
|
|
@ -266,6 +266,7 @@ type Metadata struct {
|
||||||
Primary string `protobuf:"bytes,4,opt,name=primary,proto3" json:"primary,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"`
|
SourceHost string `protobuf:"bytes,5,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
|
||||||
LoginUrl string `protobuf:"bytes,6,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
|
LoginUrl string `protobuf:"bytes,6,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
|
||||||
|
Tag string `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
@ -338,6 +339,13 @@ func (m *Metadata) GetLoginUrl() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Metadata) GetTag() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Tag
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type Credential struct {
|
type Credential struct {
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
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"`
|
CreatedAt *timestamp.Timestamp `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||||
|
@ -348,6 +356,7 @@ type Credential struct {
|
||||||
Password string `protobuf:"bytes,7,opt,name=password,proto3" json:"password,omitempty"`
|
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"`
|
SourceHost string `protobuf:"bytes,8,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
|
||||||
LoginUrl string `protobuf:"bytes,9,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
|
LoginUrl string `protobuf:"bytes,9,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
|
||||||
|
Tag string `protobuf:"bytes,10,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
@ -441,6 +450,13 @@ func (m *Credential) GetLoginUrl() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Credential) GetTag() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Tag
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type CredentialRequest struct {
|
type CredentialRequest struct {
|
||||||
Primary string `protobuf:"bytes,1,opt,name=primary,proto3" json:"primary,omitempty"`
|
Primary string `protobuf:"bytes,1,opt,name=primary,proto3" json:"primary,omitempty"`
|
||||||
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
|
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
|
||||||
|
@ -448,6 +464,7 @@ type CredentialRequest struct {
|
||||||
Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,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"`
|
SourceHost string `protobuf:"bytes,5,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
|
||||||
LoginUrl string `protobuf:"bytes,6,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
|
LoginUrl string `protobuf:"bytes,6,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
|
||||||
|
Tag string `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
@ -520,6 +537,13 @@ func (m *CredentialRequest) GetLoginUrl() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *CredentialRequest) GetTag() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Tag
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*DeleteResponse)(nil), "selfpass.credentials.DeleteResponse")
|
proto.RegisterType((*DeleteResponse)(nil), "selfpass.credentials.DeleteResponse")
|
||||||
proto.RegisterType((*GetAllMetadataRequest)(nil), "selfpass.credentials.GetAllMetadataRequest")
|
proto.RegisterType((*GetAllMetadataRequest)(nil), "selfpass.credentials.GetAllMetadataRequest")
|
||||||
|
@ -535,41 +559,41 @@ func init() {
|
||||||
func init() { proto.RegisterFile("credentials/protobuf/service.proto", fileDescriptor_ad34efc7bbd96e69) }
|
func init() { proto.RegisterFile("credentials/protobuf/service.proto", fileDescriptor_ad34efc7bbd96e69) }
|
||||||
|
|
||||||
var fileDescriptor_ad34efc7bbd96e69 = []byte{
|
var fileDescriptor_ad34efc7bbd96e69 = []byte{
|
||||||
// 539 bytes of a gzipped FileDescriptorProto
|
// 535 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xc1, 0x6e, 0xd3, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0xcf, 0x6e, 0xd3, 0x40,
|
||||||
0x10, 0x95, 0x93, 0x34, 0x75, 0xa6, 0x21, 0x12, 0xab, 0x22, 0x59, 0xae, 0x44, 0xa2, 0x05, 0x89,
|
0x10, 0xc6, 0xe5, 0xa4, 0x4d, 0xed, 0x69, 0x88, 0x60, 0x55, 0x24, 0xcb, 0x95, 0x48, 0x64, 0x90,
|
||||||
0xaa, 0x48, 0x2e, 0x2a, 0x17, 0x38, 0x86, 0x16, 0xa5, 0x1c, 0x00, 0xc9, 0xa5, 0x17, 0x2e, 0xd1,
|
0xa8, 0x8a, 0xe4, 0xa2, 0x72, 0x81, 0x63, 0x68, 0x51, 0xca, 0x01, 0x21, 0xb9, 0xf4, 0xc2, 0x25,
|
||||||
0xd6, 0x9e, 0xa6, 0x96, 0x6c, 0xaf, 0xd9, 0x5d, 0x83, 0xfa, 0x01, 0xfc, 0x13, 0xfc, 0x11, 0x9f,
|
0xda, 0xda, 0xd3, 0xd4, 0x92, 0xed, 0x35, 0xbb, 0x63, 0x50, 0x1f, 0x8c, 0x97, 0x80, 0x37, 0xe2,
|
||||||
0x81, 0xbc, 0xf6, 0x26, 0xae, 0xeb, 0x34, 0xe1, 0xc6, 0xcd, 0xb3, 0x7e, 0x6f, 0xf5, 0xde, 0xcc,
|
0x84, 0xbc, 0xfe, 0x93, 0x3f, 0x75, 0x9a, 0x48, 0x5c, 0xb8, 0xed, 0xac, 0xbf, 0x59, 0xcd, 0xef,
|
||||||
0xce, 0x03, 0x1a, 0x08, 0x0c, 0x31, 0x55, 0x11, 0x8b, 0xe5, 0x71, 0x26, 0xb8, 0xe2, 0x57, 0xf9,
|
0xdb, 0xf5, 0x07, 0x6e, 0x20, 0x31, 0xc4, 0x94, 0x22, 0x1e, 0xab, 0x93, 0x4c, 0x0a, 0x12, 0xd7,
|
||||||
0xf5, 0xb1, 0x44, 0xf1, 0x3d, 0x0a, 0xd0, 0xd3, 0x07, 0x64, 0x5f, 0x62, 0x7c, 0x9d, 0x31, 0x29,
|
0xf9, 0xcd, 0x89, 0x42, 0xf9, 0x3d, 0x0a, 0xd0, 0xd3, 0x1b, 0xec, 0x40, 0x61, 0x7c, 0x93, 0x71,
|
||||||
0xbd, 0x1a, 0xd8, 0x1d, 0x2f, 0x38, 0x5f, 0xc4, 0xb8, 0x22, 0xa9, 0x28, 0x41, 0xa9, 0x58, 0x92,
|
0xa5, 0xbc, 0x05, 0xb1, 0x33, 0x9c, 0x09, 0x31, 0x8b, 0x71, 0xde, 0x44, 0x51, 0x82, 0x8a, 0x78,
|
||||||
0x95, 0x34, 0x7a, 0x04, 0xa3, 0x33, 0x8c, 0x51, 0xa1, 0x8f, 0x32, 0xe3, 0xa9, 0x44, 0xe2, 0xc0,
|
0x92, 0x95, 0x6d, 0xee, 0x31, 0x0c, 0xce, 0x31, 0x46, 0x42, 0x1f, 0x55, 0x26, 0x52, 0x85, 0xcc,
|
||||||
0xae, 0xcc, 0x83, 0x00, 0xa5, 0x74, 0xac, 0x89, 0x75, 0x68, 0xfb, 0xa6, 0xa4, 0x6f, 0xe0, 0xc9,
|
0x86, 0x3d, 0x95, 0x07, 0x01, 0x2a, 0x65, 0x1b, 0x23, 0xe3, 0xc8, 0xf4, 0xeb, 0xd2, 0x7d, 0x0b,
|
||||||
0x0c, 0xd5, 0x34, 0x8e, 0x3f, 0xa2, 0x62, 0x21, 0x53, 0xcc, 0xc7, 0x6f, 0x39, 0x4a, 0x45, 0xc6,
|
0x4f, 0x27, 0x48, 0xe3, 0x38, 0xfe, 0x84, 0xc4, 0x43, 0x4e, 0xdc, 0xc7, 0x6f, 0x39, 0x2a, 0x62,
|
||||||
0xb0, 0x27, 0x79, 0x2e, 0x02, 0x9c, 0xdf, 0x70, 0xa9, 0x34, 0x6d, 0xe0, 0x43, 0x79, 0x74, 0xce,
|
0x43, 0xd8, 0x57, 0x22, 0x97, 0x01, 0x4e, 0x6f, 0x85, 0x22, 0xdd, 0x66, 0xf9, 0x50, 0x6e, 0x5d,
|
||||||
0xa5, 0xa2, 0x07, 0x30, 0xf8, 0x10, 0x1a, 0xf4, 0x08, 0x3a, 0x51, 0x58, 0x81, 0x3a, 0x51, 0x48,
|
0x08, 0x45, 0xee, 0x21, 0x58, 0x1f, 0xc3, 0x5a, 0x3d, 0x80, 0x4e, 0x14, 0x56, 0xa2, 0x4e, 0x14,
|
||||||
0x6f, 0xe0, 0xd1, 0x65, 0x16, 0xb2, 0x42, 0x42, 0x2b, 0x80, 0xcc, 0x00, 0x56, 0x9e, 0x9c, 0xce,
|
0xba, 0xb7, 0xf0, 0xe8, 0x2a, 0x0b, 0x79, 0x31, 0x42, 0xab, 0x80, 0x4d, 0x00, 0xe6, 0x4c, 0x76,
|
||||||
0xc4, 0x3a, 0xdc, 0x3b, 0x79, 0xe1, 0xb5, 0xf9, 0xf5, 0x4e, 0x97, 0xdf, 0xd5, 0x65, 0x7e, 0x8d,
|
0x67, 0x64, 0x1c, 0xed, 0x9f, 0xbe, 0xf4, 0xda, 0x78, 0xbd, 0xb3, 0x66, 0x5d, 0x1d, 0xe6, 0x2f,
|
||||||
0x4a, 0x8f, 0x60, 0x78, 0x96, 0x27, 0xd9, 0xd2, 0xaa, 0x0b, 0x76, 0xc0, 0x53, 0x85, 0xa9, 0x2a,
|
0xb4, 0xba, 0xc7, 0xd0, 0x3f, 0xcf, 0x93, 0xac, 0x41, 0x75, 0xc0, 0x0c, 0x44, 0x4a, 0x98, 0x52,
|
||||||
0xbd, 0x0e, 0xfd, 0x65, 0x4d, 0x47, 0x30, 0x7c, 0x9f, 0x64, 0xea, 0xb6, 0xba, 0x87, 0xfe, 0xb1,
|
0xc9, 0xda, 0xf7, 0x9b, 0xda, 0x1d, 0x40, 0xff, 0x43, 0x92, 0xd1, 0x5d, 0x75, 0x8e, 0xfb, 0xc7,
|
||||||
0xc0, 0x36, 0xbe, 0xef, 0x29, 0x7c, 0xab, 0x15, 0x32, 0x85, 0xe1, 0x9c, 0xa9, 0x4a, 0xa1, 0xeb,
|
0x00, 0xb3, 0xe6, 0xbe, 0x37, 0xe1, 0x3b, 0x3d, 0x21, 0x27, 0x0c, 0xa7, 0x9c, 0xaa, 0x09, 0x1d,
|
||||||
0x95, 0xbd, 0xf7, 0x4c, 0xef, 0xbd, 0x2f, 0xa6, 0xf7, 0xfe, 0xa0, 0x42, 0x4f, 0x55, 0x41, 0xcd,
|
0xaf, 0xf4, 0xde, 0xab, 0xbd, 0xf7, 0xbe, 0xd4, 0xde, 0xfb, 0x56, 0xa5, 0x1e, 0x53, 0xd1, 0x9a,
|
||||||
0xb5, 0x7b, 0x4d, 0xed, 0x6e, 0xa6, 0x56, 0xe8, 0xa9, 0x2a, 0x26, 0x95, 0x89, 0x28, 0x61, 0xe2,
|
0x6b, 0x7a, 0xdd, 0xda, 0xdd, 0xdc, 0x5a, 0xa9, 0xc7, 0x54, 0xdc, 0x54, 0x26, 0xa3, 0x84, 0xcb,
|
||||||
0xd6, 0xe9, 0x69, 0x29, 0xa6, 0x6c, 0x0e, 0x64, 0xa7, 0x39, 0x10, 0x72, 0x00, 0x83, 0x98, 0x2f,
|
0x3b, 0x7b, 0x47, 0x8f, 0x52, 0x97, 0xab, 0x17, 0xb2, 0xbb, 0x7a, 0x21, 0xec, 0x10, 0xac, 0x58,
|
||||||
0xa2, 0x74, 0x9e, 0x8b, 0xd8, 0xe9, 0xeb, 0xdf, 0xb6, 0x3e, 0xb8, 0x14, 0x31, 0xfd, 0xd5, 0x01,
|
0xcc, 0xa2, 0x74, 0x9a, 0xcb, 0xd8, 0xee, 0xe9, 0xcf, 0xa6, 0xde, 0xb8, 0x92, 0x31, 0x7b, 0x0c,
|
||||||
0x58, 0x35, 0xf2, 0xbf, 0x37, 0xeb, 0x82, 0x9d, 0x4b, 0x14, 0x29, 0x4b, 0xb0, 0x72, 0xba, 0xac,
|
0x5d, 0xe2, 0x33, 0x7b, 0x4f, 0x6f, 0x17, 0x4b, 0xf7, 0x57, 0x07, 0x60, 0x6e, 0xed, 0x7f, 0x8f,
|
||||||
0xc9, 0x3e, 0xec, 0x60, 0xc2, 0x22, 0xe3, 0xb1, 0x2c, 0x0a, 0x46, 0xf1, 0x72, 0x7e, 0x70, 0x11,
|
0xef, 0x80, 0x99, 0x2b, 0x94, 0x29, 0x4f, 0xb0, 0x62, 0x6f, 0x6a, 0x76, 0x00, 0xbb, 0x98, 0xf0,
|
||||||
0x3a, 0xbb, 0x25, 0xc3, 0xd4, 0xcd, 0xd6, 0xd9, 0x0f, 0xb7, 0x6e, 0xd0, 0x68, 0xdd, 0x6f, 0x0b,
|
0xa8, 0xa6, 0x2e, 0x8b, 0xa2, 0xa3, 0x78, 0x4b, 0x3f, 0x84, 0x0c, 0x2b, 0xee, 0xa6, 0x5e, 0x35,
|
||||||
0x1e, 0xdf, 0x7b, 0x83, 0x75, 0xed, 0xd6, 0x7a, 0xed, 0x9d, 0x75, 0xda, 0xbb, 0xeb, 0xb4, 0xf7,
|
0xd3, 0x7c, 0xd8, 0x4c, 0xab, 0xdd, 0x4c, 0x98, 0x9b, 0xf9, 0xdb, 0x80, 0x27, 0xf7, 0xde, 0xe9,
|
||||||
0x1e, 0xd6, 0xfe, 0x6f, 0x63, 0x3f, 0xf9, 0xd9, 0xab, 0x6b, 0xbf, 0x28, 0xd3, 0x85, 0xcc, 0x61,
|
0x22, 0x8d, 0xb1, 0x9e, 0xa6, 0xb3, 0x8e, 0xa6, 0xbb, 0x8e, 0x66, 0xe7, 0x61, 0x9a, 0x7f, 0x7d,
|
||||||
0x74, 0x77, 0xe9, 0xc9, 0xcb, 0xf6, 0xd5, 0x6b, 0x8d, 0x06, 0xf7, 0x69, 0x3b, 0xd8, 0xc0, 0x5e,
|
0x1a, 0xa7, 0x3f, 0xbb, 0x8b, 0x34, 0x97, 0x65, 0x26, 0xb1, 0x29, 0x0c, 0x96, 0xa3, 0x82, 0xbd,
|
||||||
0x59, 0xe4, 0x1c, 0xba, 0x33, 0x54, 0x64, 0xdc, 0x0e, 0x5c, 0xc6, 0x86, 0x3b, 0xd9, 0xb4, 0xf1,
|
0x6a, 0xff, 0x61, 0x5b, 0x03, 0xc5, 0x79, 0xd6, 0x2e, 0xae, 0x65, 0xaf, 0x0d, 0x76, 0x01, 0xdd,
|
||||||
0xe4, 0x02, 0xfa, 0xa7, 0xfa, 0xa9, 0x91, 0x6d, 0xd3, 0x61, 0x8b, 0x4b, 0x3f, 0x43, 0xbf, 0x4c,
|
0x09, 0x12, 0x1b, 0xb6, 0x0b, 0x9b, 0xb0, 0x71, 0x46, 0x9b, 0x72, 0x82, 0x5d, 0x42, 0xef, 0x4c,
|
||||||
0x27, 0xf2, 0xac, 0x1d, 0x7b, 0x27, 0xbb, 0xb6, 0xbb, 0xb0, 0x4c, 0xdc, 0xcd, 0x96, 0x9f, 0xb7,
|
0x3f, 0x47, 0xb6, 0x6d, 0xa6, 0x6c, 0x71, 0xe8, 0x67, 0xe8, 0x95, 0x99, 0xc6, 0x9e, 0xb7, 0x6b,
|
||||||
0x03, 0x1a, 0x81, 0xfd, 0x09, 0x7a, 0x45, 0xaa, 0x11, 0xda, 0x8e, 0xae, 0xa7, 0x98, 0xbb, 0x06,
|
0x97, 0x12, 0x6f, 0xbb, 0x03, 0xcb, 0x9c, 0xde, 0x8c, 0xfc, 0xa2, 0x5d, 0xb0, 0x1c, 0xf3, 0xef,
|
||||||
0x53, 0x4f, 0xc5, 0x77, 0xf0, 0xd5, 0x36, 0xab, 0x78, 0xd5, 0xd7, 0x5f, 0xaf, 0xff, 0x06, 0x00,
|
0xe1, 0xab, 0x59, 0xff, 0x5e, 0xd7, 0x3d, 0xbd, 0x7a, 0xf3, 0x37, 0x00, 0x00, 0xff, 0xff, 0x22,
|
||||||
0x00, 0xff, 0xff, 0x39, 0xb2, 0x93, 0x8d, 0x82, 0x06, 0x00, 0x00,
|
0xb6, 0x48, 0x2d, 0x68, 0x06, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -589,7 +613,6 @@ type CredentialServiceClient interface {
|
||||||
Create(ctx context.Context, in *CredentialRequest, 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)
|
Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Credential, error)
|
||||||
Delete(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*DeleteResponse, error)
|
Delete(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*DeleteResponse, error)
|
||||||
Dump(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*DumpResponse, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type credentialServiceClient struct {
|
type credentialServiceClient struct {
|
||||||
|
@ -668,15 +691,6 @@ func (c *credentialServiceClient) Delete(ctx context.Context, in *IdRequest, opt
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *credentialServiceClient) Dump(ctx context.Context, in *EmptyRequest, opts ...grpc.CallOption) (*DumpResponse, error) {
|
|
||||||
out := new(DumpResponse)
|
|
||||||
err := c.cc.Invoke(ctx, "/selfpass.credentials.CredentialService/Dump", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CredentialServiceServer is the server API for CredentialService service.
|
// CredentialServiceServer is the server API for CredentialService service.
|
||||||
type CredentialServiceServer interface {
|
type CredentialServiceServer interface {
|
||||||
GetAllMetadata(*GetAllMetadataRequest, CredentialService_GetAllMetadataServer) error
|
GetAllMetadata(*GetAllMetadataRequest, CredentialService_GetAllMetadataServer) error
|
||||||
|
@ -684,7 +698,6 @@ type CredentialServiceServer interface {
|
||||||
Create(context.Context, *CredentialRequest) (*Credential, error)
|
Create(context.Context, *CredentialRequest) (*Credential, error)
|
||||||
Update(context.Context, *UpdateRequest) (*Credential, error)
|
Update(context.Context, *UpdateRequest) (*Credential, error)
|
||||||
Delete(context.Context, *IdRequest) (*DeleteResponse, error)
|
Delete(context.Context, *IdRequest) (*DeleteResponse, error)
|
||||||
Dump(context.Context, *EmptyRequest) (*DumpResponse, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnimplementedCredentialServiceServer can be embedded to have forward compatible implementations.
|
// UnimplementedCredentialServiceServer can be embedded to have forward compatible implementations.
|
||||||
|
@ -706,9 +719,6 @@ func (*UnimplementedCredentialServiceServer) Update(ctx context.Context, req *Up
|
||||||
func (*UnimplementedCredentialServiceServer) Delete(ctx context.Context, req *IdRequest) (*DeleteResponse, error) {
|
func (*UnimplementedCredentialServiceServer) Delete(ctx context.Context, req *IdRequest) (*DeleteResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
|
||||||
}
|
}
|
||||||
func (*UnimplementedCredentialServiceServer) Dump(ctx context.Context, req *EmptyRequest) (*DumpResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Dump not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterCredentialServiceServer(s *grpc.Server, srv CredentialServiceServer) {
|
func RegisterCredentialServiceServer(s *grpc.Server, srv CredentialServiceServer) {
|
||||||
s.RegisterService(&_CredentialService_serviceDesc, srv)
|
s.RegisterService(&_CredentialService_serviceDesc, srv)
|
||||||
|
@ -807,24 +817,6 @@ func _CredentialService_Delete_Handler(srv interface{}, ctx context.Context, dec
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _CredentialService_Dump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(EmptyRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(CredentialServiceServer).Dump(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/selfpass.credentials.CredentialService/Dump",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(CredentialServiceServer).Dump(ctx, req.(*EmptyRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _CredentialService_serviceDesc = grpc.ServiceDesc{
|
var _CredentialService_serviceDesc = grpc.ServiceDesc{
|
||||||
ServiceName: "selfpass.credentials.CredentialService",
|
ServiceName: "selfpass.credentials.CredentialService",
|
||||||
HandlerType: (*CredentialServiceServer)(nil),
|
HandlerType: (*CredentialServiceServer)(nil),
|
||||||
|
@ -845,10 +837,6 @@ var _CredentialService_serviceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "Delete",
|
MethodName: "Delete",
|
||||||
Handler: _CredentialService_Delete_Handler,
|
Handler: _CredentialService_Delete_Handler,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
MethodName: "Dump",
|
|
||||||
Handler: _CredentialService_Dump_Handler,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@ service CredentialService {
|
||||||
rpc Create (CredentialRequest) returns (Credential);
|
rpc Create (CredentialRequest) returns (Credential);
|
||||||
rpc Update (UpdateRequest) returns (Credential);
|
rpc Update (UpdateRequest) returns (Credential);
|
||||||
rpc Delete (IdRequest) returns (DeleteResponse);
|
rpc Delete (IdRequest) returns (DeleteResponse);
|
||||||
rpc Dump (EmptyRequest) returns (DumpResponse);
|
// rpc Dump (EmptyRequest) returns (DumpResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
message DeleteResponse {
|
message DeleteResponse {
|
||||||
|
@ -46,6 +46,7 @@ message Metadata {
|
||||||
string primary = 4;
|
string primary = 4;
|
||||||
string source_host = 5;
|
string source_host = 5;
|
||||||
string login_url = 6;
|
string login_url = 6;
|
||||||
|
string tag = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Credential {
|
message Credential {
|
||||||
|
@ -58,6 +59,7 @@ message Credential {
|
||||||
string password = 7;
|
string password = 7;
|
||||||
string source_host = 8;
|
string source_host = 8;
|
||||||
string login_url = 9;
|
string login_url = 9;
|
||||||
|
string tag = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CredentialRequest {
|
message CredentialRequest {
|
||||||
|
@ -67,4 +69,5 @@ message CredentialRequest {
|
||||||
string password = 4;
|
string password = 4;
|
||||||
string source_host = 5;
|
string source_host = 5;
|
||||||
string login_url = 6;
|
string login_url = 6;
|
||||||
|
string tag = 7;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/mitchell/selfpass/credentials/endpoints"
|
||||||
|
"github.com/mitchell/selfpass/credentials/protobuf"
|
||||||
|
"github.com/mitchell/selfpass/credentials/transport"
|
||||||
|
"github.com/mitchell/selfpass/credentials/types"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCredentialServiceClient(ctx context.Context, target, ca, cert, key string) (types.CredentialClient, error) {
|
||||||
|
keypair, err := tls.X509KeyPair([]byte(cert), []byte(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
capool := x509.NewCertPool()
|
||||||
|
capool.AppendCertsFromPEM([]byte(ca))
|
||||||
|
|
||||||
|
creds := credentials.NewTLS(&tls.Config{
|
||||||
|
RootCAs: capool,
|
||||||
|
Certificates: []tls.Certificate{keypair},
|
||||||
|
})
|
||||||
|
|
||||||
|
conn, err := grpc.DialContext(ctx, target, grpc.WithTransportCredentials(creds), grpc.WithBlock())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return CredentialServiceClient{
|
||||||
|
client: protobuf.NewCredentialServiceClient(conn),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CredentialServiceClient struct {
|
||||||
|
client protobuf.CredentialServiceClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CredentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
|
||||||
|
pbmdch := make(chan protobuf.Metadata, 1)
|
||||||
|
errch = make(chan error, 1)
|
||||||
|
|
||||||
|
stream, err := transport.DecodeMetdataStreamResponse(ctx, transport.ProtobufMetadataStream{
|
||||||
|
Metadata: pbmdch,
|
||||||
|
Errors: errch,
|
||||||
|
})
|
||||||
|
|
||||||
|
srv, err := c.client.GetAllMetadata(ctx, &protobuf.GetAllMetadataRequest{SourceHost: sourceHost})
|
||||||
|
if err != nil {
|
||||||
|
errch <- err
|
||||||
|
return nil, errch
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(pbmdch)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
errch <- fmt.Errorf("context timeout")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
pbmd, err := srv.Recv()
|
||||||
|
if err == io.EOF {
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
errch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pbmdch <- *pbmd
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return stream.Metadata, stream.Errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CredentialServiceClient) Get(ctx context.Context, id string) (output types.Credential, err error) {
|
||||||
|
req := transport.EncodeIdRequest(endpoints.IDRequest{ID: id})
|
||||||
|
|
||||||
|
res, err := c.client.Get(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return output, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport.DecodeCredential(*res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CredentialServiceClient) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
|
||||||
|
req := transport.EncodeCredentialRequest(ci)
|
||||||
|
|
||||||
|
res, err := c.client.Create(ctx, &req)
|
||||||
|
if err != nil {
|
||||||
|
return output, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport.DecodeCredential(*res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CredentialServiceClient) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CredentialServiceClient) Delete(ctx context.Context, id string) (err error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
|
@ -7,18 +7,11 @@ import (
|
||||||
"github.com/mitchell/selfpass/credentials/types"
|
"github.com/mitchell/selfpass/credentials/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRedisConn(cfg ConnConfig) (c RedisConn, err error) {
|
func NewRedisConn(networkType, address string, connCount uint, options ...radix.PoolOpt) (c RedisConn, err error) {
|
||||||
p, err := radix.NewPool(cfg.NetworkType, cfg.Address, int(cfg.Size), cfg.Options...)
|
p, err := radix.NewPool(networkType, address, int(connCount), options...)
|
||||||
return RedisConn{p: p}, err
|
return RedisConn{p: p}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnConfig struct {
|
|
||||||
NetworkType string
|
|
||||||
Address string
|
|
||||||
Size uint
|
|
||||||
Options []radix.PoolOpt
|
|
||||||
}
|
|
||||||
|
|
||||||
type RedisConn struct {
|
type RedisConn struct {
|
||||||
p *radix.Pool
|
p *radix.Pool
|
||||||
}
|
}
|
||||||
|
@ -30,22 +23,23 @@ func (conn RedisConn) GetAllMetadata(ctx context.Context, sourceHost string, err
|
||||||
defer close(mdch)
|
defer close(mdch)
|
||||||
|
|
||||||
var key string
|
var key string
|
||||||
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: sourceHost + star})
|
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: types.TypePrefixCred + dash + sourceHost + star})
|
||||||
|
|
||||||
for scr.Next(&key) {
|
for scr.Next(&key) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
var md types.Metadata
|
|
||||||
|
|
||||||
if err := conn.p.Do(radix.Cmd(&md, hGetAll, key)); err != nil {
|
|
||||||
errch <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mdch <- md
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var md types.Metadata
|
||||||
|
|
||||||
|
if err := conn.p.Do(radix.Cmd(&md, hGetAll, key)); err != nil {
|
||||||
|
errch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mdch <- md
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -53,41 +47,17 @@ func (conn RedisConn) GetAllMetadata(ctx context.Context, sourceHost string, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn RedisConn) Get(ctx context.Context, id string) (output types.Credential, err error) {
|
func (conn RedisConn) Get(ctx context.Context, id string) (output types.Credential, err error) {
|
||||||
var key string
|
err = conn.p.Do(radix.Cmd(&output, hGetAll, id))
|
||||||
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: star + id, Count: 1})
|
|
||||||
|
|
||||||
if !scr.Next(&key) {
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = scr.Close(); err != nil {
|
|
||||||
return output, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = conn.p.Do(radix.Cmd(&output, hGetAll, key))
|
|
||||||
|
|
||||||
return output, err
|
return output, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn RedisConn) Put(ctx context.Context, c types.Credential) (err error) {
|
func (conn RedisConn) Put(ctx context.Context, c types.Credential) (err error) {
|
||||||
err = conn.p.Do(radix.FlatCmd(nil, hMSet, c.SourceHost+dash+c.ID, c))
|
err = conn.p.Do(radix.FlatCmd(nil, hMSet, c.ID, c))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn RedisConn) Delete(ctx context.Context, id string) (err error) {
|
func (conn RedisConn) Delete(ctx context.Context, id string) (err error) {
|
||||||
var key string
|
err = conn.p.Do(radix.Cmd(nil, del, id))
|
||||||
scr := radix.NewScanner(conn.p, radix.ScanOpts{Command: scan, Pattern: star + id, Count: 1})
|
|
||||||
|
|
||||||
if !scr.Next(&key) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = scr.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = conn.p.Do(radix.Cmd(nil, del, key))
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/mitchell/selfpass/credentials/types"
|
"github.com/mitchell/selfpass/credentials/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (ou
|
||||||
}
|
}
|
||||||
|
|
||||||
var c types.Credential
|
var c types.Credential
|
||||||
c.ID = "cred-" + uuid.New().String()
|
c.ID = generateID(ci)
|
||||||
c.CreatedAt = time.Now()
|
c.CreatedAt = time.Now()
|
||||||
c.UpdatedAt = time.Now()
|
c.UpdatedAt = time.Now()
|
||||||
c.Primary = ci.Primary
|
c.Primary = ci.Primary
|
||||||
|
@ -47,6 +46,7 @@ func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (ou
|
||||||
c.Username = ci.Username
|
c.Username = ci.Username
|
||||||
c.Email = ci.Email
|
c.Email = ci.Email
|
||||||
c.Password = ci.Password
|
c.Password = ci.Password
|
||||||
|
c.Tag = ci.Tag
|
||||||
|
|
||||||
err = svc.repo.Put(ctx, c)
|
err = svc.repo.Put(ctx, c)
|
||||||
|
|
||||||
|
@ -57,6 +57,8 @@ func validateCredentialInput(c types.CredentialInput) (err error) {
|
||||||
switch {
|
switch {
|
||||||
case c.SourceHost == "":
|
case c.SourceHost == "":
|
||||||
return fmt.Errorf("%s must specify source host", types.InvalidArgument)
|
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 == "":
|
case c.Password == "":
|
||||||
return fmt.Errorf("%s must specify password", types.InvalidArgument)
|
return fmt.Errorf("%s must specify password", types.InvalidArgument)
|
||||||
}
|
}
|
||||||
|
@ -64,6 +66,17 @@ func validateCredentialInput(c types.CredentialInput) (err error) {
|
||||||
return err
|
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) {
|
func (svc Credentials) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
|
||||||
if err = validateCredentialInput(ci); err != nil {
|
if err = validateCredentialInput(ci); err != nil {
|
||||||
return output, err
|
return output, err
|
||||||
|
@ -78,6 +91,7 @@ func (svc Credentials) Update(ctx context.Context, id string, ci types.Credentia
|
||||||
return output, err
|
return output, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.ID = generateID(ci)
|
||||||
c.UpdatedAt = time.Now()
|
c.UpdatedAt = time.Now()
|
||||||
c.Primary = ci.Primary
|
c.Primary = ci.Primary
|
||||||
c.LoginURL = ci.LoginURL
|
c.LoginURL = ci.LoginURL
|
||||||
|
@ -85,6 +99,7 @@ func (svc Credentials) Update(ctx context.Context, id string, ci types.Credentia
|
||||||
c.Password = ci.Password
|
c.Password = ci.Password
|
||||||
c.Email = ci.Email
|
c.Email = ci.Email
|
||||||
c.Username = ci.Username
|
c.Username = ci.Username
|
||||||
|
c.Tag = ci.Tag
|
||||||
|
|
||||||
return c, svc.repo.Put(ctx, c)
|
return c, svc.repo.Put(ctx, c)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,13 @@ func decodeGetAllMetadataRequest(ctx context.Context, request interface{}) (inte
|
||||||
}, nil
|
}, 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) {
|
func encodeDumpResponse(ctx context.Context, response interface{}) (interface{}, error) {
|
||||||
r := response.(endpoints.DumpResponse)
|
r := response.(endpoints.DumpResponse)
|
||||||
return protobuf.DumpResponse{
|
return protobuf.DumpResponse{
|
||||||
|
@ -50,28 +57,69 @@ func encodeMetadataStreamResponse(ctx context.Context, response interface{}) (in
|
||||||
SourceHost: md.SourceHost,
|
SourceHost: md.SourceHost,
|
||||||
Primary: md.Primary,
|
Primary: md.Primary,
|
||||||
LoginUrl: md.LoginURL,
|
LoginUrl: md.LoginURL,
|
||||||
|
Tag: md.Tag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return protobufMetadataStream{
|
return ProtobufMetadataStream{
|
||||||
Metadata: pbmdch,
|
Metadata: pbmdch,
|
||||||
Errors: r.Errors,
|
Errors: r.Errors,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type protobufMetadataStream struct {
|
func DecodeMetdataStreamResponse(ctx context.Context, r ProtobufMetadataStream) (endpoints.MetadataStream, error) {
|
||||||
|
mdch := make(chan types.Metadata, 1)
|
||||||
|
errch := make(chan error, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(mdch)
|
||||||
|
|
||||||
|
for pbmd := range r.Metadata {
|
||||||
|
createdAt, err := ptypes.Timestamp(pbmd.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
errch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedAt, err := ptypes.Timestamp(pbmd.UpdatedAt)
|
||||||
|
if err != nil {
|
||||||
|
errch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mdch <- types.Metadata{
|
||||||
|
ID: pbmd.Id,
|
||||||
|
SourceHost: pbmd.SourceHost,
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
UpdatedAt: updatedAt,
|
||||||
|
Primary: pbmd.Primary,
|
||||||
|
LoginURL: pbmd.LoginUrl,
|
||||||
|
Tag: pbmd.Tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return endpoints.MetadataStream{
|
||||||
|
Metadata: mdch,
|
||||||
|
Errors: errch,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProtobufMetadataStream struct {
|
||||||
Metadata <-chan protobuf.Metadata
|
Metadata <-chan protobuf.Metadata
|
||||||
Errors chan error
|
Errors chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeCredentialRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
func decodeCredentialRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
||||||
r := request.(protobuf.CredentialRequest)
|
r := request.(protobuf.CredentialRequest)
|
||||||
|
|
||||||
return types.CredentialInput{
|
return types.CredentialInput{
|
||||||
MetadataInput: types.MetadataInput{
|
MetadataInput: types.MetadataInput{
|
||||||
Primary: r.Primary,
|
Primary: r.Primary,
|
||||||
LoginURL: r.LoginUrl,
|
LoginURL: r.LoginUrl,
|
||||||
SourceHost: r.SourceHost,
|
SourceHost: r.SourceHost,
|
||||||
|
Tag: r.Tag,
|
||||||
},
|
},
|
||||||
Username: r.Username,
|
Username: r.Username,
|
||||||
Email: r.Email,
|
Email: r.Email,
|
||||||
|
@ -79,6 +127,18 @@ func decodeCredentialRequest(ctx context.Context, request interface{}) (interfac
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EncodeCredentialRequest(r types.CredentialInput) protobuf.CredentialRequest {
|
||||||
|
return protobuf.CredentialRequest{
|
||||||
|
Primary: r.Primary,
|
||||||
|
Username: r.Username,
|
||||||
|
Email: r.Email,
|
||||||
|
Password: r.Password,
|
||||||
|
SourceHost: r.SourceHost,
|
||||||
|
LoginUrl: r.LoginURL,
|
||||||
|
Tag: r.Tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func encodeCredentialResponse(ctx context.Context, response interface{}) (interface{}, error) {
|
func encodeCredentialResponse(ctx context.Context, response interface{}) (interface{}, error) {
|
||||||
r := response.(types.Credential)
|
r := response.(types.Credential)
|
||||||
|
|
||||||
|
@ -99,14 +159,44 @@ func encodeCredentialResponse(ctx context.Context, response interface{}) (interf
|
||||||
Primary: r.Primary,
|
Primary: r.Primary,
|
||||||
SourceHost: r.SourceHost,
|
SourceHost: r.SourceHost,
|
||||||
LoginUrl: r.LoginURL,
|
LoginUrl: r.LoginURL,
|
||||||
|
Tag: r.Tag,
|
||||||
Username: r.Username,
|
Username: r.Username,
|
||||||
Email: r.Email,
|
Email: r.Email,
|
||||||
Password: r.Password,
|
Password: r.Password,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecodeCredential(r protobuf.Credential) (c types.Credential, err error) {
|
||||||
|
|
||||||
|
createdAt, err := ptypes.Timestamp(r.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedAt, err := ptypes.Timestamp(r.UpdatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.Credential{
|
||||||
|
Metadata: types.Metadata{
|
||||||
|
ID: r.Id,
|
||||||
|
SourceHost: r.SourceHost,
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
UpdatedAt: updatedAt,
|
||||||
|
Primary: r.Primary,
|
||||||
|
LoginURL: r.LoginUrl,
|
||||||
|
Tag: r.Tag,
|
||||||
|
},
|
||||||
|
Username: r.Username,
|
||||||
|
Email: r.Email,
|
||||||
|
Password: r.Password,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
||||||
r := request.(protobuf.UpdateRequest)
|
r := request.(protobuf.UpdateRequest)
|
||||||
|
|
||||||
return endpoints.UpdateRequest{
|
return endpoints.UpdateRequest{
|
||||||
ID: r.Id,
|
ID: r.Id,
|
||||||
Credential: types.CredentialInput{
|
Credential: types.CredentialInput{
|
||||||
|
@ -114,6 +204,7 @@ func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
|
||||||
Primary: r.Credential.Primary,
|
Primary: r.Credential.Primary,
|
||||||
SourceHost: r.Credential.SourceHost,
|
SourceHost: r.Credential.SourceHost,
|
||||||
LoginURL: r.Credential.LoginUrl,
|
LoginURL: r.Credential.LoginUrl,
|
||||||
|
Tag: r.Credential.Tag,
|
||||||
},
|
},
|
||||||
Username: r.Credential.Username,
|
Username: r.Credential.Username,
|
||||||
Email: r.Credential.Email,
|
Email: r.Credential.Email,
|
||||||
|
@ -122,6 +213,24 @@ func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EncodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
||||||
|
r := request.(endpoints.UpdateRequest)
|
||||||
|
|
||||||
|
c := r.Credential
|
||||||
|
return protobuf.UpdateRequest{
|
||||||
|
Id: r.ID,
|
||||||
|
Credential: &protobuf.CredentialRequest{
|
||||||
|
Primary: c.Primary,
|
||||||
|
Username: c.Username,
|
||||||
|
Email: c.Email,
|
||||||
|
Password: c.Password,
|
||||||
|
SourceHost: c.SourceHost,
|
||||||
|
LoginUrl: c.LoginURL,
|
||||||
|
Tag: c.Tag,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
||||||
r := request.(protobuf.IdRequest)
|
r := request.(protobuf.IdRequest)
|
||||||
return endpoints.IDRequest{
|
return endpoints.IDRequest{
|
||||||
|
@ -129,6 +238,12 @@ func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, err
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func noOp(ctx context.Context, request interface{}) (interface{}, error) {
|
func EncodeIdRequest(r endpoints.IDRequest) protobuf.IdRequest {
|
||||||
|
return protobuf.IdRequest{
|
||||||
|
Id: r.ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func noOp(context.Context, interface{}) (interface{}, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (s GRPCServer) GetAllMetadata(r *protobuf.GetAllMetadataRequest, srv protob
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mds := i.(protobufMetadataStream)
|
mds := i.(ProtobufMetadataStream)
|
||||||
|
|
||||||
receiveLoop:
|
receiveLoop:
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -4,11 +4,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const TypePrefixCred = "cred"
|
||||||
|
|
||||||
type Credential struct {
|
type Credential struct {
|
||||||
Metadata
|
Metadata
|
||||||
Username string
|
Username string
|
||||||
Email string
|
Email string
|
||||||
Password string
|
Password string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CredentialInput struct {
|
type CredentialInput struct {
|
||||||
|
@ -25,10 +27,12 @@ type Metadata struct {
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
Primary string
|
Primary string
|
||||||
LoginURL string
|
LoginURL string
|
||||||
|
Tag string
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetadataInput struct {
|
type MetadataInput struct {
|
||||||
Primary string
|
Primary string
|
||||||
SourceHost string
|
SourceHost string
|
||||||
LoginURL string
|
LoginURL string
|
||||||
|
Tag string
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,3 +18,13 @@ type CredentialRepo interface {
|
||||||
Delete(ctx context.Context, id string) (err error)
|
Delete(ctx context.Context, id string) (err error)
|
||||||
DumpDB(ctx context.Context) (bs []byte, 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)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/cloudflare/redoctober/padding"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateKeyFromPassword(pass []byte) ([]byte, error) {
|
||||||
|
if len(pass) < 8 {
|
||||||
|
return nil, fmt.Errorf("master password must be at least 8 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx := 0; len(pass) < 32; idx++ {
|
||||||
|
pass = append(pass, pass[idx])
|
||||||
|
|
||||||
|
if idx == len(pass) {
|
||||||
|
idx = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pass, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CombinePasswordAndKey(pass, key []byte) ([]byte, error) {
|
||||||
|
if len(pass) < 8 {
|
||||||
|
return nil, fmt.Errorf("master password must be at least 8 characters")
|
||||||
|
}
|
||||||
|
if len(key) != 16 {
|
||||||
|
return nil, fmt.Errorf("key was not of length 16")
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx := 0; len(pass) < 16; idx++ {
|
||||||
|
pass = append(pass, pass[idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(pass[:16], key...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBCEncrypt(key []byte, plaintext []byte) ([]byte, error) {
|
||||||
|
if len(key) != 32 {
|
||||||
|
return nil, fmt.Errorf("key is not 32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext = padding.AddPadding(plaintext)
|
||||||
|
|
||||||
|
if len(plaintext)%aes.BlockSize != 0 {
|
||||||
|
return nil, fmt.Errorf("plaintext is not a multiple of the block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||||
|
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBCDecrypt(key []byte, ciphertext []byte) ([]byte, error) {
|
||||||
|
if len(key) != 32 {
|
||||||
|
return nil, fmt.Errorf("key is not 32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ciphertext) < aes.BlockSize {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
|
||||||
|
ciphertext = ciphertext[aes.BlockSize:]
|
||||||
|
|
||||||
|
if len(ciphertext)%aes.BlockSize != 0 {
|
||||||
|
return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
mode.CryptBlocks(ciphertext, ciphertext)
|
||||||
|
|
||||||
|
return padding.RemovePadding(ciphertext)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: "redis:alpine"
|
||||||
|
selfpass:
|
||||||
|
build: .
|
||||||
|
entrypoint:
|
||||||
|
- server
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
secrets:
|
||||||
|
- ca_cert
|
||||||
|
- server_cert
|
||||||
|
- server_key
|
||||||
|
secrets:
|
||||||
|
ca_cert:
|
||||||
|
file: ./certs/ca.pem
|
||||||
|
server_cert:
|
||||||
|
file: ./certs/server.pem
|
||||||
|
server_key:
|
||||||
|
file: ./certs/server-key.pem
|
36
dual-entry
36
dual-entry
|
@ -1,36 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Start the first process
|
|
||||||
redis-server ./redis.conf &
|
|
||||||
status=$?
|
|
||||||
if [ $status -ne 0 ]; then
|
|
||||||
echo "Failed to start my_first_process: $status"
|
|
||||||
exit $status
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start the second process
|
|
||||||
./server --dev -v &
|
|
||||||
status=$?
|
|
||||||
if [ $status -ne 0 ]; then
|
|
||||||
echo "Failed to start my_second_process: $status"
|
|
||||||
exit $status
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Naive check runs checks once a minute to see if either of the processes exited.
|
|
||||||
# This illustrates part of the heavy lifting you need to do if you want to run
|
|
||||||
# more than one service in a container. The container exits with an error
|
|
||||||
# if it detects that either of the processes has exited.
|
|
||||||
# Otherwise it loops forever, waking up every 60 seconds
|
|
||||||
|
|
||||||
while sleep 60; do
|
|
||||||
ps aux |grep my_first_process |grep -q -v grep
|
|
||||||
PROCESS_1_STATUS=$?
|
|
||||||
ps aux |grep my_second_process |grep -q -v grep
|
|
||||||
PROCESS_2_STATUS=$?
|
|
||||||
# If the greps above find anything, they exit with 0 status
|
|
||||||
# If they are not both 0, then something is wrong
|
|
||||||
if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
|
|
||||||
echo "One of the processes has already exited."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
24
go.mod
24
go.mod
|
@ -1,15 +1,33 @@
|
||||||
module github.com/mitchell/selfpass
|
module github.com/mitchell/selfpass
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b // indirect
|
||||||
|
github.com/atotto/clipboard v0.1.2
|
||||||
github.com/aws/aws-sdk-go-v2 v0.7.0
|
github.com/aws/aws-sdk-go-v2 v0.7.0
|
||||||
|
github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692
|
||||||
github.com/go-kit/kit v0.8.0
|
github.com/go-kit/kit v0.8.0
|
||||||
github.com/go-logfmt/logfmt v0.4.0 // indirect
|
github.com/go-logfmt/logfmt v0.4.0 // indirect
|
||||||
github.com/go-stack/stack v1.8.0 // indirect
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
github.com/golang/protobuf v1.3.1
|
github.com/golang/protobuf v1.3.1
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
|
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/kr/pty v1.1.4 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.1 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.7 // indirect
|
||||||
github.com/mediocregopher/radix/v3 v3.2.3
|
github.com/mediocregopher/radix/v3 v3.2.3
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
|
github.com/spf13/cobra v0.0.3
|
||||||
|
github.com/spf13/pflag v1.0.3
|
||||||
|
github.com/spf13/viper v1.3.2
|
||||||
github.com/stretchr/testify v1.3.0 // indirect
|
github.com/stretchr/testify v1.3.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect
|
||||||
golang.org/x/sys v0.0.0-20190416152802-12500544f89f // indirect
|
golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect
|
||||||
google.golang.org/grpc v1.20.0
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb // indirect
|
||||||
|
golang.org/x/text v0.3.2 // indirect
|
||||||
|
google.golang.org/appengine v1.4.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52 // indirect
|
||||||
|
google.golang.org/grpc v1.20.1
|
||||||
|
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
||||||
)
|
)
|
||||||
|
|
108
go.sum
108
go.sum
|
@ -1,12 +1,26 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
||||||
|
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||||
|
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b h1:sSQK05nvxs4UkgCJaxihteu+r+6ela3dNMm7NVmsS3c=
|
||||||
|
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=
|
||||||
|
github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aws/aws-sdk-go-v2 v0.7.0 h1:a5xRI/tBmUFKuAA0SOyEY2P1YhQb+jVOEI9P/7KfrP0=
|
github.com/aws/aws-sdk-go-v2 v0.7.0 h1:a5xRI/tBmUFKuAA0SOyEY2P1YhQb+jVOEI9P/7KfrP0=
|
||||||
github.com/aws/aws-sdk-go-v2 v0.7.0/go.mod h1:17MaCZ9g0q5BIMxwzRQeiv8M3c8+W7iuBnlWAEprcxE=
|
github.com/aws/aws-sdk-go-v2 v0.7.0/go.mod h1:17MaCZ9g0q5BIMxwzRQeiv8M3c8+W7iuBnlWAEprcxE=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692 h1:eVvf+nrm0mXV3JOh2c9vt+Pemh/TAUOjqRNrp1eyPmk=
|
||||||
|
github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||||
|
@ -14,8 +28,6 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
@ -25,45 +37,123 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gucumber/gucumber v0.0.0-20180127021336-7d5c79e832a2/go.mod h1:YbdHRK9ViqwGMS0rtRY+1I6faHvVyyurKPIPwifihxI=
|
github.com/gucumber/gucumber v0.0.0-20180127021336-7d5c79e832a2/go.mod h1:YbdHRK9ViqwGMS0rtRY+1I6faHvVyyurKPIPwifihxI=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
|
||||||
|
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||||
|
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c h1:kp3AxgXgDOmIJFR7bIwqFhwJ2qWar8tEQSE5XXhCfVk=
|
||||||
|
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
|
||||||
|
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed h1:3dQJqqDouawQgl3gBE1PNHKFkJYGEuFb1DbSlaxdosE=
|
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed h1:3dQJqqDouawQgl3gBE1PNHKFkJYGEuFb1DbSlaxdosE=
|
||||||
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
|
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 h1:TbcGCZdo9zfPYPgevsqRn+OjvCyfOK6TzuXhqzWdCt0=
|
||||||
github.com/mediocregopher/radix/v3 v3.2.3/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
|
github.com/mediocregopher/radix/v3 v3.2.3/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
|
||||||
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||||
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
||||||
|
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||||
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
|
||||||
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
|
||||||
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI=
|
||||||
|
golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190416152802-12500544f89f h1:1ZH9RnjNgLzh6YrsRp/c6ddZ8Lq0fq9xztNOoWJ2sz4=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190416152802-12500544f89f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb h1:k07iPOt0d6nEnwXF+kHB+iEg+WSuKe/SOQuFM2QoD+E=
|
||||||
|
golang.org/x/sys v0.0.0-20190516110030-61b9204099cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/grpc v1.20.0 h1:DlsSIrgEBuZAUFJcta2B5i/lzeHHbnfkNFAfFXLVFYQ=
|
google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52 h1:LHc/6x2dMeCKkSsrVgo4DY+Z566T1OeoMwLtdfoy8LE=
|
||||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
google.golang.org/genproto v0.0.0-20190516172635-bb713bdc0e52/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
|
||||||
|
gopkg.in/AlecAivazis/survey.v1 v1.8.4/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
@ -3,4 +3,4 @@ grpcurl -cacert ./certs/ca.pem \
|
||||||
-key ./certs/client-key.pem \
|
-key ./certs/client-key.pem \
|
||||||
-proto ./credentials/protobuf/service.proto \
|
-proto ./credentials/protobuf/service.proto \
|
||||||
localhost:8080 \
|
localhost:8080 \
|
||||||
selfpass.credentials.CredentialService/Dump
|
selfpass.credentials.CredentialService/GetAllMetadata
|
||||||
|
|
1317
redis.conf
1317
redis.conf
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue