2019-04-15 03:56:55 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-07-18 04:56:12 +00:00
|
|
|
"encoding/base64"
|
2019-05-06 00:56:27 +00:00
|
|
|
"fmt"
|
2019-07-20 08:12:58 +00:00
|
|
|
"math/rand"
|
2019-04-15 03:56:55 +00:00
|
|
|
"time"
|
|
|
|
|
2019-07-11 02:33:22 +00:00
|
|
|
"github.com/mitchell/selfpass/services/credentials/types"
|
2019-04-15 03:56:55 +00:00
|
|
|
)
|
|
|
|
|
2019-07-12 01:02:46 +00:00
|
|
|
func NewCredentials(repo types.CredentialsRepo) Credentials {
|
2019-04-15 03:56:55 +00:00
|
|
|
return Credentials{
|
|
|
|
repo: repo,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Credentials struct {
|
2019-07-12 01:02:46 +00:00
|
|
|
repo types.CredentialsRepo
|
2019-04-15 03:56:55 +00:00
|
|
|
}
|
|
|
|
|
2019-05-06 00:56:27 +00:00
|
|
|
func (svc Credentials) GetAllMetadata(ctx context.Context, sourceHost string) (output <-chan types.Metadata, errch chan error) {
|
2019-04-15 03:56:55 +00:00
|
|
|
errch = make(chan error, 1)
|
2019-05-06 00:56:27 +00:00
|
|
|
output = svc.repo.GetAllMetadata(ctx, sourceHost, errch)
|
2019-04-15 03:56:55 +00:00
|
|
|
return output, errch
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc Credentials) Get(ctx context.Context, id string) (output types.Credential, err error) {
|
2019-05-06 00:56:27 +00:00
|
|
|
if id == "" {
|
|
|
|
return output, fmt.Errorf("%s must specify an id", types.InvalidArgument)
|
|
|
|
}
|
|
|
|
return svc.repo.Get(ctx, id)
|
2019-04-15 03:56:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (svc Credentials) Create(ctx context.Context, ci types.CredentialInput) (output types.Credential, err error) {
|
2019-05-06 00:56:27 +00:00
|
|
|
if err = validateCredentialInput(ci); err != nil {
|
2019-04-15 03:56:55 +00:00
|
|
|
return output, err
|
|
|
|
}
|
|
|
|
|
2019-07-20 08:12:58 +00:00
|
|
|
now := time.Now()
|
|
|
|
|
2019-04-15 03:56:55 +00:00
|
|
|
var c types.Credential
|
2019-07-20 08:12:58 +00:00
|
|
|
c.ID = generateID()
|
|
|
|
c.CreatedAt = now
|
|
|
|
c.UpdatedAt = now
|
2019-04-15 03:56:55 +00:00
|
|
|
c.Primary = ci.Primary
|
|
|
|
c.LoginURL = ci.LoginURL
|
|
|
|
c.SourceHost = ci.SourceHost
|
|
|
|
c.Username = ci.Username
|
|
|
|
c.Email = ci.Email
|
|
|
|
c.Password = ci.Password
|
2019-05-28 01:16:50 +00:00
|
|
|
c.OTPSecret = ci.OTPSecret
|
2019-05-22 15:22:40 +00:00
|
|
|
c.Tag = ci.Tag
|
2019-04-15 03:56:55 +00:00
|
|
|
|
|
|
|
err = svc.repo.Put(ctx, c)
|
|
|
|
|
|
|
|
return c, err
|
|
|
|
}
|
|
|
|
|
2019-05-06 00:56:27 +00:00
|
|
|
func validateCredentialInput(c types.CredentialInput) (err error) {
|
|
|
|
switch {
|
|
|
|
case c.SourceHost == "":
|
|
|
|
return fmt.Errorf("%s must specify source host", types.InvalidArgument)
|
2019-05-22 15:22:40 +00:00
|
|
|
case c.Primary == "":
|
|
|
|
return fmt.Errorf("%s must specify primary user key", types.InvalidArgument)
|
2019-05-06 00:56:27 +00:00
|
|
|
case c.Password == "":
|
|
|
|
return fmt.Errorf("%s must specify password", types.InvalidArgument)
|
|
|
|
}
|
|
|
|
|
2019-07-18 04:56:12 +00:00
|
|
|
if _, err = base64.StdEncoding.DecodeString(c.Password); err != nil {
|
|
|
|
return fmt.Errorf("%s password must be encrypted and base64 encoded", types.InvalidArgument)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.OTPSecret != "" {
|
|
|
|
if _, err = base64.StdEncoding.DecodeString(c.OTPSecret); err != nil {
|
|
|
|
return fmt.Errorf("%s otp secret must be encrypted and base64 encoded", types.InvalidArgument)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-15 03:56:55 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-20 08:12:58 +00:00
|
|
|
func generateID() string {
|
|
|
|
const idLen = 8
|
|
|
|
const alphanumerics = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789"
|
|
|
|
const alphaLen = len(alphanumerics)
|
2019-05-22 15:22:40 +00:00
|
|
|
|
2019-07-20 08:12:58 +00:00
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
id := make([]byte, idLen)
|
|
|
|
|
|
|
|
for index := range id {
|
|
|
|
id[index] = alphanumerics[rand.Int63()%int64(alphaLen)]
|
2019-05-22 15:22:40 +00:00
|
|
|
}
|
|
|
|
|
2019-07-20 08:12:58 +00:00
|
|
|
return fmt.Sprintf("%s-%s", types.KeyCredential, string(id))
|
2019-05-22 15:22:40 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 03:56:55 +00:00
|
|
|
func (svc Credentials) Update(ctx context.Context, id string, ci types.CredentialInput) (output types.Credential, err error) {
|
2019-05-06 00:56:27 +00:00
|
|
|
if err = validateCredentialInput(ci); err != nil {
|
|
|
|
return output, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if id == "" {
|
|
|
|
return output, fmt.Errorf("%s must specify an id", types.InvalidArgument)
|
|
|
|
}
|
|
|
|
|
2019-04-15 03:56:55 +00:00
|
|
|
c, err := svc.repo.Get(ctx, id)
|
|
|
|
if err != nil {
|
|
|
|
return output, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.UpdatedAt = time.Now()
|
|
|
|
c.Primary = ci.Primary
|
|
|
|
c.LoginURL = ci.LoginURL
|
|
|
|
c.SourceHost = ci.SourceHost
|
|
|
|
c.Password = ci.Password
|
2019-05-28 01:16:50 +00:00
|
|
|
c.OTPSecret = ci.OTPSecret
|
2019-04-15 03:56:55 +00:00
|
|
|
c.Email = ci.Email
|
|
|
|
c.Username = ci.Username
|
2019-05-22 15:22:40 +00:00
|
|
|
c.Tag = ci.Tag
|
2019-04-15 03:56:55 +00:00
|
|
|
|
|
|
|
return c, svc.repo.Put(ctx, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc Credentials) Delete(ctx context.Context, id string) (err error) {
|
2019-05-06 00:56:27 +00:00
|
|
|
if id == "" {
|
|
|
|
return fmt.Errorf("%s must specify an id", types.InvalidArgument)
|
|
|
|
}
|
|
|
|
return svc.repo.Delete(ctx, id)
|
|
|
|
}
|