Implemented all but update from cli client to server;

solidified encryption;
setup deployment mechanism for GCP
This commit is contained in:
mitchell 2019-05-27 18:16:50 -07:00
parent cd24f6e848
commit c5ae0b4ddc
28 changed files with 598 additions and 295 deletions

4
.gitignore vendored
View File

@ -1,4 +1,6 @@
.idea
bin
certs
db
data
*.tar
*.enc

View File

@ -2,9 +2,10 @@ FROM golang:1.11.5 as build
WORKDIR /go/src/github.com/mitchell/selfpass
COPY . .
ENV GO111MODULE=on
RUN make gen-certs-go
RUN make build
FROM debian:stable-20190326-slim
FROM debian:stable-20190506-slim
WORKDIR /usr/bin
COPY --from=build /go/src/github.com/mitchell/selfpass/bin/server .
ENTRYPOINT ["server"]

View File

@ -1,7 +1,8 @@
.PHONY: all build clean format test docker-build
build: clean format test
go build --o ./bin/server ./cmd/server/server.go
build: clean format
env CGO_ENABLED=0 go build -o ./bin/server ./cmd/server
rm ./cmd/server/certs.go
clean:
rm -rf ./bin
@ -10,9 +11,32 @@ clean:
docker:
docker-compose build
start:
up:
docker-compose up
upd:
docker-compose up -d
down:
docker-compose down
machine-create-google:
docker-machine create --driver google \
--google-address m-selfpass \
--google-project selfpass-241808 \
--google-machine-type n1-standard-1 \
--google-machine-image https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-9-stretch-v20190514 \
selfpass01
machine-rm:
docker-machine rm selfpass01
machine-ssh:
docker-machine ssh selfpass01
machine-env:
docker-machine env selfpass01
format:
gofmt -w -s -l .
@ -36,5 +60,8 @@ gen-server-cert:
gen-client-cert:
cd certs && cfssl gencert -ca ca.pem -ca-key ca-key.pem csr.json | cfssljson -bare client
gen-certs-go:
./gen_certs_go.sh > ./cmd/server/certs.go
test:
go test -cover ./...

52
cli/commands/decrypt.go Normal file
View File

@ -0,0 +1,52 @@
package commands
import (
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/mitchell/selfpass/credentials/commands"
"github.com/mitchell/selfpass/crypto"
)
func makeDecrypt(masterpass string, cfg *viper.Viper) *cobra.Command {
decryptCmd := &cobra.Command{
Use: "decrypt [file]",
Short: "Decrypt a file using your masterpass and secret key",
Long: `Decrypt a file using your masterpass and secret key, and replace the old file with
the new file.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
file := args[0]
fileout := file
if file[len(file)-4:] == ".enc" {
fileout = file[:len(file)-4]
}
contents, err := ioutil.ReadFile(file)
check(err)
key, err := hex.DecodeString(cfg.GetString(commands.KeyPrivateKey))
check(err)
passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
check(err)
contents, err = crypto.CBCDecrypt(passkey, contents)
check(err)
check(ioutil.WriteFile(fileout, contents, 0600))
check(os.Remove(file))
fmt.Println("Decrypted file: ", fileout)
},
}
return decryptCmd
}

48
cli/commands/encrypt.go Normal file
View File

@ -0,0 +1,48 @@
package commands
import (
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/mitchell/selfpass/credentials/commands"
"github.com/mitchell/selfpass/crypto"
)
func makeEncrypt(masterpass string, cfg *viper.Viper) *cobra.Command {
encryptCmd := &cobra.Command{
Use: "encrypt [file]",
Short: "Encrypt a file using your masterpass and secret key",
Long: `Encrypt a file using your masterpass and secret key, and replace the old file with the
new file.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
file := args[0]
fileEnc := file + ".enc"
contents, err := ioutil.ReadFile(file)
check(err)
key, err := hex.DecodeString(cfg.GetString(commands.KeyPrivateKey))
check(err)
passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
check(err)
contents, err = crypto.CBCEncrypt(passkey, contents)
check(err)
check(ioutil.WriteFile(fileEnc, contents, 0600))
check(os.Remove(file))
fmt.Println("Encrypted file: ", fileEnc)
},
}
return encryptCmd
}

View File

@ -1,4 +1,4 @@
package cmd
package commands
import (
"fmt"
@ -11,10 +11,10 @@ import (
"github.com/spf13/viper"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/credentials/cmds"
"github.com/mitchell/selfpass/credentials/commands"
)
func makeInitCmd(cfg *viper.Viper) *cobra.Command {
func makeInit(cfg *viper.Viper) *cobra.Command {
initCmd := &cobra.Command{
Use: "init",
Short: "This command initializes SPC for the first time",
@ -33,7 +33,7 @@ the users private key, and server certificates. (All of which will be encrypted)
privateKey = strings.Replace(uuid.New().String(), "-", "", -1)
)
prompt = &survey.Password{Message: "Master password:"}
prompt = &survey.Password{Message: "New master password:"}
check(survey.AskOne(prompt, &masterpass, nil))
prompt = &survey.Password{Message: "Confirm master password:"}
@ -69,14 +69,14 @@ the users private key, and server certificates. (All of which will be encrypted)
key, err := ioutil.ReadFile(keyFile)
check(err)
cfg.Set(cmds.KeyConnConfig, map[string]string{
cfg.Set(keyConnConfig, map[string]string{
"target": target,
"ca": string(ca),
"cert": string(cert),
"key": string(key),
})
cfg.Set(cmds.KeyPrivateKey, privateKey)
cfg.Set(commands.KeyPrivateKey, privateKey)
if err := cfg.WriteConfig(); err != nil {
home, err := homedir.Dir()

View File

@ -1,56 +1,67 @@
package cmd
package commands
import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"
"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/credentials/commands"
credtypes "github.com/mitchell/selfpass/credentials/types"
"github.com/mitchell/selfpass/crypto"
)
func Execute(ctx context.Context, initClient types.CredentialClientInit) {
func Execute(initClient credtypes.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.`,
Version: "v0.1.0",
}
rootCmd.InitDefaultHelpFlag()
rootCmd.InitDefaultVersionFlag()
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)
decryptCfg := rootCmd.Flags().Bool("decrypt-cfg", false, "decrypt config file")
check(rootCmd.ParseFlags(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
var masterpass string
var cfg *viper.Viper
needsCfg := (len(os.Args) > 1 && !strings.Contains(strings.Join(os.Args, "--"), "--help")) || *decryptCfg
if needsCfg {
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)))
rootCmd.AddCommand(makeInit(cfg))
rootCmd.AddCommand(makeEncrypt(masterpass, cfg))
rootCmd.AddCommand(makeDecrypt(masterpass, cfg))
rootCmd.AddCommand(commands.MakeList(makeInitClient(cfg, initClient)))
rootCmd.AddCommand(commands.MakeCreate(masterpass, cfg, makeInitClient(cfg, initClient)))
rootCmd.AddCommand(commands.MakeGet(masterpass, cfg, makeInitClient(cfg, initClient)))
rootCmd.AddCommand(commands.MakeDelete(makeInitClient(cfg, initClient)))
check(rootCmd.Execute())
}
func makeInitClient(cfg *viper.Viper, initClient types.CredentialClientInit) cmds.CredentialClientInit {
return func(ctx context.Context) types.CredentialClient {
connConfig := cfg.GetStringMapString(cmds.KeyConnConfig)
func makeInitClient(cfg *viper.Viper, initClient credtypes.CredentialClientInit) commands.CredentialClientInit {
return func(ctx context.Context) credtypes.CredentialClient {
connConfig := cfg.GetStringMapString(keyConnConfig)
client, err := initClient(
ctx,
@ -145,3 +156,5 @@ func check(err error) {
os.Exit(1)
}
}
const keyConnConfig = "connection"

View File

@ -5,7 +5,6 @@ import (
"crypto/x509"
"flag"
"io"
"io/ioutil"
stdlog "log"
"net"
"os"
@ -28,13 +27,10 @@ var logger log.Logger
func main() {
var (
stop = make(chan os.Signal, 1)
dev = flag.Bool("dev", false, "enables dev mode logging")
port = flag.String("port", "8080", "specify the port to listen on")
crtFile = flag.String("cert", "/run/secrets/server_cert", "specify the cert file")
keyFile = flag.String("key", "/run/secrets/server_key", "specify the private key file")
caFile = flag.String("ca", "/run/secrets/ca_cert", "specify the ca cert file")
verbose = flag.Bool("v", false, "be more verbose")
stop = make(chan os.Signal, 1)
jsonLogs = flag.Bool("json-logs", false, "enables json logging")
port = flag.String("port", "8080", "specify the port to listen on")
verbose = flag.Bool("v", false, "be more verbose")
// tableName = flag.String(
// "credential-table-name",
// "selfpass-credential",
@ -47,16 +43,13 @@ func main() {
signal.Notify(stop, syscall.SIGKILL)
signal.Notify(stop, syscall.SIGTERM)
logger = newLogger(os.Stdout, *dev)
logger = newLogger(os.Stdout, *jsonLogs)
keypair, err := tls.LoadX509KeyPair(*crtFile, *keyFile)
check(err)
ca, err := ioutil.ReadFile(*caFile)
keypair, err := tls.X509KeyPair([]byte(cert), []byte(key))
check(err)
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(ca)
caPool.AppendCertsFromPEM([]byte(ca))
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{keypair},
@ -85,7 +78,6 @@ func main() {
_ = logger.Log(
"message", "serving",
"address", addr,
"dev", dev,
)
go func() { check(srv.Serve(lis)) }()
@ -95,13 +87,11 @@ func main() {
srv.GracefulStop()
}
func newLogger(writer io.Writer, dev bool) log.Logger {
var l log.Logger
func newLogger(writer io.Writer, jsonLogs bool) log.Logger {
writer = log.NewSyncWriter(writer)
l := log.NewLogfmtLogger(writer)
if dev {
l = log.NewLogfmtLogger(writer)
} else {
if jsonLogs {
l = log.NewJSONLogger(writer)
}
l = log.WithPrefix(l, "caller", log.Caller(5), "timestamp", log.DefaultTimestamp)

View File

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

View File

@ -1,63 +0,0 @@
package cmds
import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"time"
"github.com/atotto/clipboard"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/mitchell/selfpass/crypto"
)
func MakeGetCmd(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
getCmd := &cobra.Command{
Use: "get [id]",
Short: "Get a credential info and copy password to clipboard",
Long: `Get a credential's info and copy password to clipboard, from Selfpass server, after
decrypting password.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
cbcontents, err := clipboard.ReadAll()
check(err)
restore := func(cbcontents string) {
time.Sleep(time.Second * 5)
clipboard.WriteAll(cbcontents)
}
cred, err := initClient(ctx).Get(ctx, args[0])
check(err)
key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
check(err)
passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), key)
check(err)
passbytes, err := base64.StdEncoding.DecodeString(cred.Password)
check(err)
plainpass, err := crypto.CBCDecrypt(passkey, passbytes)
check(clipboard.WriteAll(string(plainpass)))
go restore(cbcontents)
cjson, err := json.MarshalIndent(cred, "", " ")
check(err)
fmt.Println(string(cjson))
fmt.Println("Wrote password to clipboard.")
},
}
return getCmd
}

View File

@ -1,4 +1,4 @@
package cmds
package commands
import (
"context"
@ -17,5 +17,4 @@ func check(err error) {
}
}
const KeyConnConfig = "connection"
const KeyPrivateKey = "private_key"

View File

@ -1,16 +1,15 @@
package cmds
package commands
import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"os"
"time"
"github.com/atotto/clipboard"
"github.com/pquerna/otp/totp"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/AlecAivazis/survey.v1"
@ -19,7 +18,11 @@ import (
"github.com/mitchell/selfpass/crypto"
)
func MakeCreateCmd(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
func MakeCreate(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
var length uint
var numbers bool
var specials bool
createCmd := &cobra.Command{
Use: "create",
Short: "Create a credential in Selfpass",
@ -56,16 +59,21 @@ password.`,
},
}
var ci types.CredentialInput
check(survey.Ask(mdqs, &ci.MetadataInput))
check(survey.Ask(cqs, &ci))
key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
check(err)
keypass, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
check(err)
var newpass bool
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)
ci.Password = crypto.GeneratePassword(int(length), numbers, specials)
var copypass bool
prompt = &survey.Confirm{Message: "Copy new pass to clipboard?", Default: true}
@ -79,7 +87,7 @@ password.`,
check(survey.AskOne(prompt, &ci.Password, nil))
var cpass string
prompt = &survey.Password{Message: "Confirm assword: "}
prompt = &survey.Password{Message: "Confirm password: "}
check(survey.AskOne(prompt, &cpass, nil))
if ci.Password != cpass {
@ -88,59 +96,58 @@ password.`,
}
}
key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
check(err)
keypass, err := crypto.CombinePasswordAndKey([]byte(masterpass), []byte(key))
check(err)
cipherpass, err := crypto.CBCEncrypt(keypass, []byte(ci.Password))
check(err)
ci.Password = base64.StdEncoding.EncodeToString(cipherpass)
var otp bool
prompt = &survey.Confirm{Message: "Do you have an OTP/MFA secret?", Default: true}
check(survey.AskOne(prompt, &otp, nil))
if otp {
var secret string
prompt := &survey.Password{Message: "OTP secret:"}
check(survey.AskOne(prompt, &secret, nil))
ciphersecret, err := crypto.CBCEncrypt(keypass, []byte(secret))
check(err)
ci.OTPSecret = base64.StdEncoding.EncodeToString(ciphersecret)
var copyotp bool
prompt2 := &survey.Confirm{Message: "Copy new OTP to clipboard?", Default: true}
check(survey.AskOne(prompt2, &copyotp, nil))
if copyotp {
otp, err := totp.GenerateCode(secret, time.Now())
check(err)
check(clipboard.WriteAll(otp))
}
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
c, err := initClient(ctx).Create(ctx, ci)
check(err)
mdjson, err := json.MarshalIndent(c.Metadata, "", " ")
check(err)
fmt.Println(string(mdjson))
fmt.Println(c)
var cleancb bool
prompt = &survey.Confirm{Message: "Do you want to clear the clipboard?", Default: true}
check(survey.AskOne(prompt, &cleancb, nil))
if cleancb {
check(clipboard.WriteAll(" "))
}
},
}
createCmd.Flags().BoolVarP(&numbers, "numbers", "n", true, "use numbers in the generated password")
createCmd.Flags().BoolVarP(&specials, "specials", "s", false, "use special characters in the generated password")
createCmd.Flags().UintVarP(&length, "length", "l", 32, "length of the generated password")
return createCmd
}
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)
}

View File

@ -0,0 +1,33 @@
package commands
import (
"context"
"time"
"github.com/spf13/cobra"
"gopkg.in/AlecAivazis/survey.v1"
)
func MakeDelete(initConfig CredentialClientInit) *cobra.Command {
deleteCmd := &cobra.Command{
Use: "delete [id]",
Short: "Delete a credential using the given ID",
Long: `Delete a credential using the given ID, permanently. THERE IS NO UNDOING THIS ACTION.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var confirmed bool
prompt := &survey.Confirm{Message: "Are you sure you want to permanently delete this credential?"}
check(survey.AskOne(prompt, &confirmed, nil))
if confirmed {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
check(initConfig(ctx).Delete(ctx, args[0]))
}
},
}
return deleteCmd
}

View File

@ -0,0 +1,92 @@
package commands
import (
"context"
"encoding/base64"
"encoding/hex"
"fmt"
"time"
"github.com/atotto/clipboard"
"github.com/pquerna/otp/totp"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/AlecAivazis/survey.v1"
"github.com/mitchell/selfpass/crypto"
)
func MakeGet(masterpass string, cfg *viper.Viper, initClient CredentialClientInit) *cobra.Command {
getCmd := &cobra.Command{
Use: "get [id]",
Short: "Get a credential info and copy password to clipboard",
Long: `Get a credential's info and copy password to clipboard, from Selfpass server, after
decrypting password.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
cred, err := initClient(ctx).Get(ctx, args[0])
check(err)
fmt.Println(cred)
check(clipboard.WriteAll(string(cred.Primary)))
fmt.Println("Wrote primary user key to clipboard.")
key, err := hex.DecodeString(cfg.GetString(KeyPrivateKey))
check(err)
passkey, err := crypto.CombinePasswordAndKey([]byte(masterpass), key)
check(err)
var copyPass bool
prompt := &survey.Confirm{Message: "Copy password to clipboard?", Default: true}
check(survey.AskOne(prompt, &copyPass, nil))
if copyPass {
passbytes, err := base64.StdEncoding.DecodeString(cred.Password)
check(err)
plainpass, err := crypto.CBCDecrypt(passkey, passbytes)
check(clipboard.WriteAll(string(plainpass)))
fmt.Println("Wrote password to clipboard.")
}
if cred.OTPSecret != "" {
var newOTP bool
prompt = &survey.Confirm{Message: "Generate one time password and copy to clipboard?", Default: true}
check(survey.AskOne(prompt, &newOTP, nil))
if newOTP {
secretbytes, err := base64.StdEncoding.DecodeString(cred.OTPSecret)
check(err)
plainsecret, err := crypto.CBCDecrypt(passkey, secretbytes)
otp, err := totp.GenerateCode(string(plainsecret), time.Now())
check(err)
check(clipboard.WriteAll(otp))
fmt.Println("Wrote one time password to clipboard.")
}
}
var cleancb bool
prompt = &survey.Confirm{Message: "Do you want to clear the clipboard?", Default: true}
check(survey.AskOne(prompt, &cleancb, nil))
if cleancb {
check(clipboard.WriteAll(" "))
}
},
}
return getCmd
}

View File

@ -1,15 +1,14 @@
package cmds
package commands
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/spf13/cobra"
)
func MakeListCmd(initClient CredentialClientInit) *cobra.Command {
func MakeList(initClient CredentialClientInit) *cobra.Command {
var sourceHost string
listCmd := &cobra.Command{
@ -38,11 +37,11 @@ includes almost all the information but the most sensitive.`,
break receive
}
mdjson, err := json.MarshalIndent(md, "", " ")
check(err)
fmt.Println(string(mdjson))
fmt.Println(md)
}
}
fmt.Println("Done listing.")
},
}

View File

@ -357,6 +357,7 @@ type Credential struct {
SourceHost string `protobuf:"bytes,8,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
LoginUrl string `protobuf:"bytes,9,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
Tag string `protobuf:"bytes,10,opt,name=tag,proto3" json:"tag,omitempty"`
OtpSecret string `protobuf:"bytes,11,opt,name=otp_secret,json=otpSecret,proto3" json:"otp_secret,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -457,6 +458,13 @@ func (m *Credential) GetTag() string {
return ""
}
func (m *Credential) GetOtpSecret() string {
if m != nil {
return m.OtpSecret
}
return ""
}
type CredentialRequest struct {
Primary string `protobuf:"bytes,1,opt,name=primary,proto3" json:"primary,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
@ -465,6 +473,7 @@ type CredentialRequest struct {
SourceHost string `protobuf:"bytes,5,opt,name=source_host,json=sourceHost,proto3" json:"source_host,omitempty"`
LoginUrl string `protobuf:"bytes,6,opt,name=login_url,json=loginUrl,proto3" json:"login_url,omitempty"`
Tag string `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"`
OtpSecret string `protobuf:"bytes,8,opt,name=otp_secret,json=otpSecret,proto3" json:"otp_secret,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -544,6 +553,13 @@ func (m *CredentialRequest) GetTag() string {
return ""
}
func (m *CredentialRequest) GetOtpSecret() string {
if m != nil {
return m.OtpSecret
}
return ""
}
func init() {
proto.RegisterType((*DeleteResponse)(nil), "selfpass.credentials.DeleteResponse")
proto.RegisterType((*GetAllMetadataRequest)(nil), "selfpass.credentials.GetAllMetadataRequest")
@ -559,41 +575,43 @@ func init() {
func init() { proto.RegisterFile("credentials/protobuf/service.proto", fileDescriptor_ad34efc7bbd96e69) }
var fileDescriptor_ad34efc7bbd96e69 = []byte{
// 535 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0xcf, 0x6e, 0xd3, 0x40,
0x10, 0xc6, 0xe5, 0xa4, 0x4d, 0xed, 0x69, 0x88, 0x60, 0x55, 0x24, 0xcb, 0x95, 0x48, 0x64, 0x90,
0xa8, 0x8a, 0xe4, 0xa2, 0x72, 0x81, 0x63, 0x68, 0x51, 0xca, 0x01, 0x21, 0xb9, 0xf4, 0xc2, 0x25,
0xda, 0xda, 0xd3, 0xd4, 0x92, 0xed, 0x35, 0xbb, 0x63, 0x50, 0x1f, 0x8c, 0x97, 0x80, 0x37, 0xe2,
0x84, 0xbc, 0xfe, 0x93, 0x3f, 0x75, 0x9a, 0x48, 0x5c, 0xb8, 0xed, 0xac, 0xbf, 0x59, 0xcd, 0xef,
0xdb, 0xf5, 0x07, 0x6e, 0x20, 0x31, 0xc4, 0x94, 0x22, 0x1e, 0xab, 0x93, 0x4c, 0x0a, 0x12, 0xd7,
0xf9, 0xcd, 0x89, 0x42, 0xf9, 0x3d, 0x0a, 0xd0, 0xd3, 0x1b, 0xec, 0x40, 0x61, 0x7c, 0x93, 0x71,
0xa5, 0xbc, 0x05, 0xb1, 0x33, 0x9c, 0x09, 0x31, 0x8b, 0x71, 0xde, 0x44, 0x51, 0x82, 0x8a, 0x78,
0x92, 0x95, 0x6d, 0xee, 0x31, 0x0c, 0xce, 0x31, 0x46, 0x42, 0x1f, 0x55, 0x26, 0x52, 0x85, 0xcc,
0x86, 0x3d, 0x95, 0x07, 0x01, 0x2a, 0x65, 0x1b, 0x23, 0xe3, 0xc8, 0xf4, 0xeb, 0xd2, 0x7d, 0x0b,
0x4f, 0x27, 0x48, 0xe3, 0x38, 0xfe, 0x84, 0xc4, 0x43, 0x4e, 0xdc, 0xc7, 0x6f, 0x39, 0x2a, 0x62,
0x43, 0xd8, 0x57, 0x22, 0x97, 0x01, 0x4e, 0x6f, 0x85, 0x22, 0xdd, 0x66, 0xf9, 0x50, 0x6e, 0x5d,
0x08, 0x45, 0xee, 0x21, 0x58, 0x1f, 0xc3, 0x5a, 0x3d, 0x80, 0x4e, 0x14, 0x56, 0xa2, 0x4e, 0x14,
0xba, 0xb7, 0xf0, 0xe8, 0x2a, 0x0b, 0x79, 0x31, 0x42, 0xab, 0x80, 0x4d, 0x00, 0xe6, 0x4c, 0x76,
0x67, 0x64, 0x1c, 0xed, 0x9f, 0xbe, 0xf4, 0xda, 0x78, 0xbd, 0xb3, 0x66, 0x5d, 0x1d, 0xe6, 0x2f,
0xb4, 0xba, 0xc7, 0xd0, 0x3f, 0xcf, 0x93, 0xac, 0x41, 0x75, 0xc0, 0x0c, 0x44, 0x4a, 0x98, 0x52,
0xc9, 0xda, 0xf7, 0x9b, 0xda, 0x1d, 0x40, 0xff, 0x43, 0x92, 0xd1, 0x5d, 0x75, 0x8e, 0xfb, 0xc7,
0x00, 0xb3, 0xe6, 0xbe, 0x37, 0xe1, 0x3b, 0x3d, 0x21, 0x27, 0x0c, 0xa7, 0x9c, 0xaa, 0x09, 0x1d,
0xaf, 0xf4, 0xde, 0xab, 0xbd, 0xf7, 0xbe, 0xd4, 0xde, 0xfb, 0x56, 0xa5, 0x1e, 0x53, 0xd1, 0x9a,
0x6b, 0x7a, 0xdd, 0xda, 0xdd, 0xdc, 0x5a, 0xa9, 0xc7, 0x54, 0xdc, 0x54, 0x26, 0xa3, 0x84, 0xcb,
0x3b, 0x7b, 0x47, 0x8f, 0x52, 0x97, 0xab, 0x17, 0xb2, 0xbb, 0x7a, 0x21, 0xec, 0x10, 0xac, 0x58,
0xcc, 0xa2, 0x74, 0x9a, 0xcb, 0xd8, 0xee, 0xe9, 0xcf, 0xa6, 0xde, 0xb8, 0x92, 0x31, 0x7b, 0x0c,
0x5d, 0xe2, 0x33, 0x7b, 0x4f, 0x6f, 0x17, 0x4b, 0xf7, 0x57, 0x07, 0x60, 0x6e, 0xed, 0x7f, 0x8f,
0xef, 0x80, 0x99, 0x2b, 0x94, 0x29, 0x4f, 0xb0, 0x62, 0x6f, 0x6a, 0x76, 0x00, 0xbb, 0x98, 0xf0,
0xa8, 0xa6, 0x2e, 0x8b, 0xa2, 0xa3, 0x78, 0x4b, 0x3f, 0x84, 0x0c, 0x2b, 0xee, 0xa6, 0x5e, 0x35,
0xd3, 0x7c, 0xd8, 0x4c, 0xab, 0xdd, 0x4c, 0x98, 0x9b, 0xf9, 0xdb, 0x80, 0x27, 0xf7, 0xde, 0xe9,
0x22, 0x8d, 0xb1, 0x9e, 0xa6, 0xb3, 0x8e, 0xa6, 0xbb, 0x8e, 0x66, 0xe7, 0x61, 0x9a, 0x7f, 0x7d,
0x1a, 0xa7, 0x3f, 0xbb, 0x8b, 0x34, 0x97, 0x65, 0x26, 0xb1, 0x29, 0x0c, 0x96, 0xa3, 0x82, 0xbd,
0x6a, 0xff, 0x61, 0x5b, 0x03, 0xc5, 0x79, 0xd6, 0x2e, 0xae, 0x65, 0xaf, 0x0d, 0x76, 0x01, 0xdd,
0x09, 0x12, 0x1b, 0xb6, 0x0b, 0x9b, 0xb0, 0x71, 0x46, 0x9b, 0x72, 0x82, 0x5d, 0x42, 0xef, 0x4c,
0x3f, 0x47, 0xb6, 0x6d, 0xa6, 0x6c, 0x71, 0xe8, 0x67, 0xe8, 0x95, 0x99, 0xc6, 0x9e, 0xb7, 0x6b,
0x97, 0x12, 0x6f, 0xbb, 0x03, 0xcb, 0x9c, 0xde, 0x8c, 0xfc, 0xa2, 0x5d, 0xb0, 0x1c, 0xf3, 0xef,
0xe1, 0xab, 0x59, 0xff, 0x5e, 0xd7, 0x3d, 0xbd, 0x7a, 0xf3, 0x37, 0x00, 0x00, 0xff, 0xff, 0x22,
0xb6, 0x48, 0x2d, 0x68, 0x06, 0x00, 0x00,
// 561 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x95, 0x4d, 0x6f, 0xd3, 0x4c,
0x10, 0xc7, 0xe5, 0xa4, 0x4d, 0xed, 0x69, 0x9f, 0xe8, 0x61, 0x55, 0x24, 0xcb, 0x15, 0x24, 0x32,
0x48, 0x54, 0x45, 0x72, 0x51, 0xb9, 0xc0, 0x31, 0xb4, 0x28, 0xe5, 0x80, 0x90, 0x1c, 0x7a, 0xe1,
0x12, 0x6d, 0xed, 0x69, 0x6a, 0xc9, 0xf6, 0x9a, 0xdd, 0x31, 0xa8, 0x1f, 0x8c, 0x0f, 0x05, 0x1f,
0x81, 0x13, 0xf2, 0xfa, 0x25, 0x2f, 0x75, 0x9a, 0x48, 0x5c, 0xb8, 0xed, 0x8c, 0xff, 0xb3, 0x9a,
0xdf, 0xec, 0xee, 0xdf, 0xe0, 0x06, 0x12, 0x43, 0x4c, 0x29, 0xe2, 0xb1, 0x3a, 0xcd, 0xa4, 0x20,
0x71, 0x9d, 0xdf, 0x9c, 0x2a, 0x94, 0xdf, 0xa2, 0x00, 0x3d, 0x9d, 0x60, 0x87, 0x0a, 0xe3, 0x9b,
0x8c, 0x2b, 0xe5, 0x2d, 0x88, 0x9d, 0xc1, 0x4c, 0x88, 0x59, 0x8c, 0xf3, 0x22, 0x8a, 0x12, 0x54,
0xc4, 0x93, 0xac, 0x2c, 0x73, 0x4f, 0xa0, 0x7f, 0x81, 0x31, 0x12, 0xfa, 0xa8, 0x32, 0x91, 0x2a,
0x64, 0x36, 0xec, 0xa9, 0x3c, 0x08, 0x50, 0x29, 0xdb, 0x18, 0x1a, 0xc7, 0xa6, 0x5f, 0x87, 0xee,
0x1b, 0x78, 0x3c, 0x46, 0x1a, 0xc5, 0xf1, 0x47, 0x24, 0x1e, 0x72, 0xe2, 0x3e, 0x7e, 0xcd, 0x51,
0x11, 0x1b, 0xc0, 0xbe, 0x12, 0xb9, 0x0c, 0x70, 0x7a, 0x2b, 0x14, 0xe9, 0x32, 0xcb, 0x87, 0x32,
0x75, 0x29, 0x14, 0xb9, 0x47, 0x60, 0x7d, 0x08, 0x6b, 0x75, 0x1f, 0x3a, 0x51, 0x58, 0x89, 0x3a,
0x51, 0xe8, 0xde, 0xc2, 0x7f, 0x57, 0x59, 0xc8, 0x8b, 0x16, 0x5a, 0x05, 0x6c, 0x0c, 0x30, 0x67,
0xb2, 0x3b, 0x43, 0xe3, 0x78, 0xff, 0xec, 0x85, 0xd7, 0xc6, 0xeb, 0x9d, 0x37, 0xeb, 0x6a, 0x33,
0x7f, 0xa1, 0xd4, 0x3d, 0x81, 0x83, 0x8b, 0x3c, 0xc9, 0x1a, 0x54, 0x07, 0xcc, 0x40, 0xa4, 0x84,
0x29, 0x95, 0xac, 0x07, 0x7e, 0x13, 0xbb, 0x7d, 0x38, 0x78, 0x9f, 0x64, 0x74, 0x57, 0xed, 0xe3,
0xfe, 0x36, 0xc0, 0xac, 0xb9, 0xef, 0x75, 0xf8, 0x56, 0x77, 0xc8, 0x09, 0xc3, 0x29, 0xa7, 0xaa,
0x43, 0xc7, 0x2b, 0x67, 0xef, 0xd5, 0xb3, 0xf7, 0x3e, 0xd7, 0xb3, 0xf7, 0xad, 0x4a, 0x3d, 0xa2,
0xa2, 0x34, 0xd7, 0xf4, 0xba, 0xb4, 0xbb, 0xb9, 0xb4, 0x52, 0x8f, 0xa8, 0x38, 0xa9, 0x4c, 0x46,
0x09, 0x97, 0x77, 0xf6, 0x8e, 0x6e, 0xa5, 0x0e, 0x57, 0x0f, 0x64, 0x77, 0xf5, 0x40, 0xd8, 0x11,
0x58, 0xb1, 0x98, 0x45, 0xe9, 0x34, 0x97, 0xb1, 0xdd, 0xd3, 0x9f, 0x4d, 0x9d, 0xb8, 0x92, 0x31,
0xfb, 0x1f, 0xba, 0xc4, 0x67, 0xf6, 0x9e, 0x4e, 0x17, 0x4b, 0xf7, 0x67, 0x07, 0x60, 0x3e, 0xda,
0x7f, 0x1e, 0xdf, 0x01, 0x33, 0x57, 0x28, 0x53, 0x9e, 0x60, 0xc5, 0xde, 0xc4, 0xec, 0x10, 0x76,
0x31, 0xe1, 0x51, 0x4d, 0x5d, 0x06, 0x45, 0x45, 0x71, 0x97, 0xbe, 0x0b, 0x19, 0x56, 0xdc, 0x4d,
0xbc, 0x3a, 0x4c, 0xf3, 0xe1, 0x61, 0x5a, 0xed, 0xc3, 0x84, 0x66, 0x98, 0xec, 0x09, 0x80, 0xa0,
0x6c, 0xaa, 0x30, 0x90, 0x48, 0xf6, 0xbe, 0xfe, 0x60, 0x09, 0xca, 0x26, 0x3a, 0xe1, 0xfe, 0x32,
0xe0, 0xd1, 0xbd, 0x6b, 0xbc, 0x08, 0x6b, 0xac, 0x87, 0xed, 0xac, 0x83, 0xed, 0xae, 0x83, 0xdd,
0x79, 0x18, 0xf6, 0x6f, 0x6f, 0xce, 0x0a, 0xac, 0xb9, 0x02, 0x7b, 0xf6, 0xa3, 0xbb, 0x08, 0x3b,
0x29, 0x1d, 0x8d, 0x4d, 0xa1, 0xbf, 0x6c, 0x34, 0xec, 0x65, 0xfb, 0x73, 0x6f, 0xb5, 0x23, 0xe7,
0x69, 0xbb, 0xb8, 0x96, 0xbd, 0x32, 0xd8, 0x25, 0x74, 0xc7, 0x48, 0x6c, 0xd0, 0x2e, 0x6c, 0xac,
0xca, 0x19, 0x6e, 0x72, 0x19, 0x36, 0x81, 0xde, 0xb9, 0xbe, 0xcc, 0x6c, 0x5b, 0x47, 0xda, 0x62,
0xd3, 0x4f, 0xd0, 0x2b, 0x1d, 0x91, 0x3d, 0x6b, 0xd7, 0x2e, 0xf9, 0xe5, 0x76, 0x1b, 0x96, 0x2e,
0xbf, 0x19, 0xf9, 0x79, 0xbb, 0x60, 0xf9, 0x27, 0xf1, 0x0e, 0xbe, 0x98, 0xf5, 0xe3, 0xbc, 0xee,
0xe9, 0xd5, 0xeb, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xde, 0x03, 0x4f, 0xb2, 0xa6, 0x06, 0x00,
0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View File

@ -60,6 +60,7 @@ message Credential {
string source_host = 8;
string login_url = 9;
string tag = 10;
string otp_secret = 11;
}
message CredentialRequest {
@ -70,4 +71,5 @@ message CredentialRequest {
string source_host = 5;
string login_url = 6;
string tag = 7;
string otp_secret = 8;
}

View File

@ -7,12 +7,13 @@ import (
"fmt"
"io"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"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) {
@ -34,16 +35,16 @@ func NewCredentialServiceClient(ctx context.Context, target, ca, cert, key strin
return nil, err
}
return CredentialServiceClient{
return credentialServiceClient{
client: protobuf.NewCredentialServiceClient(conn),
}, nil
}
type CredentialServiceClient struct {
type credentialServiceClient struct {
client protobuf.CredentialServiceClient
}
func (c CredentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
func (c credentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
pbmdch := make(chan protobuf.Metadata, 1)
errch = make(chan error, 1)
@ -84,7 +85,7 @@ func (c CredentialServiceClient) GetAllMetadata(ctx context.Context, sourceHost
return stream.Metadata, stream.Errors
}
func (c CredentialServiceClient) Get(ctx context.Context, id string) (output types.Credential, err error) {
func (c credentialServiceClient) Get(ctx context.Context, id string) (output types.Credential, err error) {
req := transport.EncodeIdRequest(endpoints.IDRequest{ID: id})
res, err := c.client.Get(ctx, &req)
@ -95,7 +96,7 @@ func (c CredentialServiceClient) Get(ctx context.Context, id string) (output typ
return transport.DecodeCredential(*res)
}
func (c CredentialServiceClient) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
func (c credentialServiceClient) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
req := transport.EncodeCredentialRequest(ci)
res, err := c.client.Create(ctx, &req)
@ -106,10 +107,26 @@ func (c CredentialServiceClient) Create(ctx context.Context, ci types.Credential
return transport.DecodeCredential(*res)
}
func (c CredentialServiceClient) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
panic("implement me")
func (c credentialServiceClient) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
req := transport.EncodeUpdateRequest(endpoints.UpdateRequest{ID: id, Credential: ci})
res, err := c.client.Update(ctx, &req)
if err != nil {
return output, err
}
return transport.DecodeCredential(*res)
}
func (c CredentialServiceClient) Delete(ctx context.Context, id string) (err error) {
panic("implement me")
func (c credentialServiceClient) Delete(ctx context.Context, id string) (err error) {
req := transport.EncodeIdRequest(endpoints.IDRequest{ID: id})
res, err := c.client.Delete(ctx, &req)
if err != nil {
return err
}
if !res.Success {
return fmt.Errorf("delete unsuccessful")
}
return nil
}

View File

@ -46,6 +46,7 @@ func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (ou
c.Username = ci.Username
c.Email = ci.Email
c.Password = ci.Password
c.OTPSecret = ci.OTPSecret
c.Tag = ci.Tag
err = svc.repo.Put(ctx, c)
@ -97,6 +98,7 @@ func (svc Credentials) Update(ctx context.Context, id string, ci types.Credentia
c.LoginURL = ci.LoginURL
c.SourceHost = ci.SourceHost
c.Password = ci.Password
c.OTPSecret = ci.OTPSecret
c.Email = ci.Email
c.Username = ci.Username
c.Tag = ci.Tag

View File

@ -4,6 +4,7 @@ import (
"context"
"github.com/golang/protobuf/ptypes"
"github.com/mitchell/selfpass/credentials/endpoints"
"github.com/mitchell/selfpass/credentials/protobuf"
"github.com/mitchell/selfpass/credentials/types"
@ -121,9 +122,10 @@ func decodeCredentialRequest(ctx context.Context, request interface{}) (interfac
SourceHost: r.SourceHost,
Tag: r.Tag,
},
Username: r.Username,
Email: r.Email,
Password: r.Password,
Username: r.Username,
Email: r.Email,
Password: r.Password,
OTPSecret: r.OtpSecret,
}, nil
}
@ -133,6 +135,7 @@ func EncodeCredentialRequest(r types.CredentialInput) protobuf.CredentialRequest
Username: r.Username,
Email: r.Email,
Password: r.Password,
OtpSecret: r.OTPSecret,
SourceHost: r.SourceHost,
LoginUrl: r.LoginURL,
Tag: r.Tag,
@ -163,6 +166,7 @@ func encodeCredentialResponse(ctx context.Context, response interface{}) (interf
Username: r.Username,
Email: r.Email,
Password: r.Password,
OtpSecret: r.OTPSecret,
}, nil
}
@ -188,9 +192,10 @@ func DecodeCredential(r protobuf.Credential) (c types.Credential, err error) {
LoginURL: r.LoginUrl,
Tag: r.Tag,
},
Username: r.Username,
Email: r.Email,
Password: r.Password,
Username: r.Username,
Email: r.Email,
Password: r.Password,
OTPSecret: r.OtpSecret,
}, nil
}
@ -206,16 +211,15 @@ func decodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
LoginURL: r.Credential.LoginUrl,
Tag: r.Credential.Tag,
},
Username: r.Credential.Username,
Email: r.Credential.Email,
Password: r.Credential.Password,
Username: r.Credential.Username,
Email: r.Credential.Email,
Password: r.Credential.Password,
OTPSecret: r.Credential.OtpSecret,
},
}, nil
}
func EncodeUpdateRequest(ctx context.Context, request interface{}) (interface{}, error) {
r := request.(endpoints.UpdateRequest)
func EncodeUpdateRequest(r endpoints.UpdateRequest) protobuf.UpdateRequest {
c := r.Credential
return protobuf.UpdateRequest{
Id: r.ID,
@ -224,11 +228,12 @@ func EncodeUpdateRequest(ctx context.Context, request interface{}) (interface{},
Username: c.Username,
Email: c.Email,
Password: c.Password,
OtpSecret: c.OTPSecret,
SourceHost: c.SourceHost,
LoginUrl: c.LoginURL,
Tag: c.Tag,
},
}, nil
}
}
func decodeIdRequest(ctx context.Context, request interface{}) (interface{}, error) {

View File

@ -1,6 +1,7 @@
package types
import (
"fmt"
"time"
)
@ -8,16 +9,25 @@ const TypePrefixCred = "cred"
type Credential struct {
Metadata
Username string
Email string
Password string `json:"-"`
Username string
Email string
Password string `json:"-"`
OTPSecret string `json:"-"`
}
func (c Credential) String() string {
return fmt.Sprintf(
"username = %s\nemail = %s\n%s",
c.Username, c.Email, c.Metadata,
)
}
type CredentialInput struct {
MetadataInput
Username string
Email string
Password string
Username string
Email string
Password string
OTPSecret string
}
type Metadata struct {
@ -30,6 +40,13 @@ type Metadata struct {
Tag string
}
func (m Metadata) String() string {
return fmt.Sprintf(
"id = %s\nsourceHost = %s\ncreatedAt = %s\nupdatedAt = %s\nprimary = %s\nloginUrl = %s\ntag = %s\n",
m.ID, m.SourceHost, m.CreatedAt, m.UpdatedAt, m.Primary, m.LoginURL, m.Tag,
)
}
type MetadataInput struct {
Primary string
SourceHost string

View File

@ -10,37 +10,6 @@ import (
"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")

68
crypto/helpers.go Normal file
View File

@ -0,0 +1,68 @@
package crypto
import (
"fmt"
"math/rand"
"time"
)
func GenerateKeyFromPassword(pass []byte) ([]byte, error) {
if len(pass) < 8 {
return nil, fmt.Errorf("master password must be at least 8 characters")
}
for idx := 0; len(pass) < 32; idx++ {
pass = append(pass, pass[idx])
if idx == len(pass) {
idx = 0
}
}
return pass, nil
}
func CombinePasswordAndKey(pass, key []byte) ([]byte, error) {
if len(pass) < 8 {
return nil, fmt.Errorf("master password must be at least 8 characters")
}
if len(key) != 16 {
return nil, fmt.Errorf("key was not of length 16")
}
for idx := 0; len(pass) < 16; idx++ {
pass = append(pass, pass[idx])
}
return append(pass[:16], key...), nil
}
func GeneratePassword(length int, numbers, specials bool) string {
const alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const alphanumerics = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
const alphasAndSpecials = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()"
const alphanumericsAndSpecials = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()"
rand.Seed(time.Now().UnixNano())
pass := make([]byte, length)
switch {
case numbers && specials:
for idx := 0; idx < length; idx++ {
pass[idx] = alphanumericsAndSpecials[rand.Int63()%int64(len(alphanumericsAndSpecials))]
}
case numbers:
for idx := 0; idx < length; idx++ {
pass[idx] = alphanumerics[rand.Int63()%int64(len(alphanumerics))]
}
case specials:
for idx := 0; idx < length; idx++ {
pass[idx] = alphasAndSpecials[rand.Int63()%int64(len(alphasAndSpecials))]
}
default:
for idx := 0; idx < length; idx++ {
pass[idx] = alphas[rand.Int63()%int64(len(alphas))]
}
}
return string(pass)
}

View File

@ -1,21 +1,16 @@
version: "3.7"
services:
redis:
image: "redis:alpine"
selfpass:
image: "redis:5.0.5"
volumes:
- "./data:/data"
server:
build: .
entrypoint:
- server
restart: always
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
entrypoint:
- server
- -v
depends_on:
- redis

13
gen_certs_go.sh Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env sh
ca=$(cat ./certs/ca.pem)
cert=$(cat ./certs/server.pem)
key=$(cat ./certs/server-key.pem)
cat << EOM
// Code generated by inject-certs.sh, DO NOT EDIT.
package main
const ca = \`${ca}\`
const cert = \`${cert}\`
const key = \`${key}\`
EOM

4
go.mod
View File

@ -4,7 +4,9 @@ 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/boombuler/barcode v1.0.0 // indirect
github.com/cloudflare/redoctober v0.0.0-20180928214028-3f826eedb692
github.com/davecgh/go-spew v1.1.1
github.com/go-kit/kit v0.8.0
github.com/go-logfmt/logfmt v0.4.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
@ -17,8 +19,8 @@ require (
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/mediocregopher/radix/v3 v3.2.3
github.com/mitchellh/go-homedir v1.1.0
github.com/pquerna/otp v1.1.0
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.3.2
github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect

4
go.sum
View File

@ -10,6 +10,8 @@ github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzb
github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/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/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/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=
@ -81,6 +83,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.1.0 h1:q2gMsMuMl3JzneUaAX1MRGxLvOG6bzXV51hivBaStf0=
github.com/pquerna/otp v1.1.0/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk=
github.com/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=

View File

@ -1,6 +0,0 @@
grpcurl -cacert ./certs/ca.pem \
-cert ./certs/client.pem \
-key ./certs/client-key.pem \
-proto ./credentials/protobuf/service.proto \
localhost:8080 \
selfpass.credentials.CredentialService/GetAllMetadata