Add gRPC vault lifecycle backend flow
This commit is contained in:
+54
-14
@@ -4,15 +4,17 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"maps"
|
||||
"os"
|
||||
"slices"
|
||||
"sync"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.julianfamily.org/keepassgo/clipboard"
|
||||
"git.julianfamily.org/keepassgo/passwords"
|
||||
keepassgov1 "git.julianfamily.org/keepassgo/proto/keepassgo/v1"
|
||||
"git.julianfamily.org/keepassgo/webdav"
|
||||
"git.julianfamily.org/keepassgo/session"
|
||||
"git.julianfamily.org/keepassgo/vault"
|
||||
"git.julianfamily.org/keepassgo/webdav"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -36,6 +38,8 @@ type lifecycleBackend interface {
|
||||
Open(string, vault.MasterKey) error
|
||||
OpenRemote(webdav.Client, string, vault.MasterKey) error
|
||||
Save() error
|
||||
Lock() error
|
||||
Unlock(vault.MasterKey) error
|
||||
}
|
||||
|
||||
func NewServer(model vault.Model, profiles map[string]passwords.Profile, clipboardWriter clipboard.Writer) *Server {
|
||||
@@ -57,8 +61,8 @@ func (s *Server) GetSessionStatus(_ context.Context, _ *keepassgov1.GetSessionSt
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return &keepassgov1.GetSessionStatusResponse{
|
||||
Locked: s.locked,
|
||||
Dirty: s.dirty,
|
||||
Locked: s.locked,
|
||||
Dirty: s.dirty,
|
||||
EntryCount: uint32(len(s.model.Entries)),
|
||||
}, nil
|
||||
}
|
||||
@@ -70,12 +74,12 @@ func (s *Server) OpenVault(_ context.Context, req *keepassgov1.OpenVaultRequest)
|
||||
|
||||
key := vault.MasterKey{Password: req.GetPassword(), KeyFileData: append([]byte(nil), req.GetKeyFileData()...)}
|
||||
if err := s.lifecycle.Open(req.GetPath(), key); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "open vault: %v", err)
|
||||
return nil, mapLifecycleError("open vault", err)
|
||||
}
|
||||
|
||||
model, err := s.lifecycle.Current()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "load opened vault: %v", err)
|
||||
return nil, mapLifecycleError("load opened vault", err)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
@@ -99,12 +103,12 @@ func (s *Server) OpenRemoteVault(_ context.Context, req *keepassgov1.OpenRemoteV
|
||||
}
|
||||
key := vault.MasterKey{Password: req.GetMasterPassword(), KeyFileData: append([]byte(nil), req.GetKeyFileData()...)}
|
||||
if err := s.lifecycle.OpenRemote(client, req.GetPath(), key); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "open remote vault: %v", err)
|
||||
return nil, mapLifecycleError("open remote vault", err)
|
||||
}
|
||||
|
||||
model, err := s.lifecycle.Current()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "load opened remote vault: %v", err)
|
||||
return nil, mapLifecycleError("load opened remote vault", err)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
@@ -122,7 +126,7 @@ func (s *Server) SaveVault(_ context.Context, _ *keepassgov1.SaveVaultRequest) (
|
||||
}
|
||||
|
||||
if err := s.lifecycle.Save(); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "save vault: %v", err)
|
||||
return nil, mapLifecycleError("save vault", err)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
@@ -133,23 +137,59 @@ func (s *Server) SaveVault(_ context.Context, _ *keepassgov1.SaveVaultRequest) (
|
||||
}
|
||||
|
||||
func (s *Server) LockVault(_ context.Context, _ *keepassgov1.LockVaultRequest) (*keepassgov1.LockVaultResponse, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.lifecycle == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "vault lifecycle backend is not configured")
|
||||
}
|
||||
|
||||
if err := s.lifecycle.Lock(); err != nil {
|
||||
return nil, mapLifecycleError("lock vault", err)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
s.locked = true
|
||||
s.mu.Unlock()
|
||||
|
||||
return &keepassgov1.LockVaultResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UnlockVault(_ context.Context, _ *keepassgov1.UnlockVaultRequest) (*keepassgov1.UnlockVaultResponse, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
func (s *Server) UnlockVault(_ context.Context, req *keepassgov1.UnlockVaultRequest) (*keepassgov1.UnlockVaultResponse, error) {
|
||||
if s.lifecycle == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "vault lifecycle backend is not configured")
|
||||
}
|
||||
|
||||
key := vault.MasterKey{Password: req.GetPassword(), KeyFileData: append([]byte(nil), req.GetKeyFileData()...)}
|
||||
if err := s.lifecycle.Unlock(key); err != nil {
|
||||
return nil, mapLifecycleError("unlock vault", err)
|
||||
}
|
||||
|
||||
model, err := s.lifecycle.Current()
|
||||
if err != nil {
|
||||
return nil, mapLifecycleError("load unlocked vault", err)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
s.model = model
|
||||
s.locked = false
|
||||
s.mu.Unlock()
|
||||
|
||||
return &keepassgov1.UnlockVaultResponse{}, nil
|
||||
}
|
||||
|
||||
func mapLifecycleError(operation string, err error) error {
|
||||
switch {
|
||||
case errors.Is(err, os.ErrNotExist):
|
||||
return status.Errorf(codes.NotFound, "%s: %v", operation, err)
|
||||
case errors.Is(err, vault.ErrInvalidMasterKey):
|
||||
return status.Errorf(codes.InvalidArgument, "%s: %v", operation, err)
|
||||
case errors.Is(err, session.ErrLocked), errors.Is(err, session.ErrNoPath):
|
||||
return status.Errorf(codes.FailedPrecondition, "%s: %v", operation, err)
|
||||
case errors.Is(err, webdav.ErrConflict):
|
||||
return status.Errorf(codes.Aborted, "%s: %v", operation, err)
|
||||
default:
|
||||
return status.Errorf(codes.Internal, "%s: %v", operation, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) ListEntries(_ context.Context, req *keepassgov1.ListEntriesRequest) (*keepassgov1.ListEntriesResponse, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
Reference in New Issue
Block a user