123 lines
2.4 KiB
Go
123 lines
2.4 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
|
|
"git.julianfamily.org/keepassgo/internal/clipboard"
|
|
"git.julianfamily.org/keepassgo/internal/passwords"
|
|
"git.julianfamily.org/keepassgo/internal/session"
|
|
"git.julianfamily.org/keepassgo/internal/vault"
|
|
keepassgov1 "git.julianfamily.org/keepassgo/proto/keepassgo/v1"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
type DirtyProvider func() bool
|
|
|
|
type Host struct {
|
|
server *Server
|
|
grpcServer *grpc.Server
|
|
listener net.Listener
|
|
lifecycle lifecycleBackend
|
|
dirty DirtyProvider
|
|
mu sync.Mutex
|
|
lastModel vault.Model
|
|
started bool
|
|
listenAddr string
|
|
}
|
|
|
|
func StartHost(addr string, lifecycle lifecycleBackend, profiles map[string]passwords.Profile, clipboardWriter clipboard.Writer, dirty DirtyProvider) (*Host, error) {
|
|
addr = strings.TrimSpace(addr)
|
|
if addr == "" || strings.EqualFold(addr, "off") {
|
|
return nil, nil
|
|
}
|
|
|
|
listener, err := net.Listen("tcp", addr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("listen gRPC host %s: %w", addr, err)
|
|
}
|
|
|
|
service := NewServerWithLifecycle(vault.Model{}, profiles, clipboardWriter, lifecycle)
|
|
server := grpc.NewServer(grpc.UnaryInterceptor(AuthInterceptor(service)))
|
|
keepassgov1.RegisterVaultServiceServer(server, service)
|
|
|
|
host := &Host{
|
|
server: service,
|
|
grpcServer: server,
|
|
listener: listener,
|
|
lifecycle: lifecycle,
|
|
dirty: dirty,
|
|
listenAddr: listener.Addr().String(),
|
|
started: true,
|
|
}
|
|
if err := host.SyncFromLifecycle(); err != nil && !errors.Is(err, session.ErrLocked) {
|
|
_ = listener.Close()
|
|
server.Stop()
|
|
return nil, err
|
|
}
|
|
|
|
go func() {
|
|
_ = server.Serve(listener)
|
|
}()
|
|
|
|
return host, nil
|
|
}
|
|
|
|
func (h *Host) Address() string {
|
|
if h == nil {
|
|
return ""
|
|
}
|
|
return h.listenAddr
|
|
}
|
|
|
|
func (h *Host) Server() *Server {
|
|
if h == nil {
|
|
return nil
|
|
}
|
|
return h.server
|
|
}
|
|
|
|
func (h *Host) Stop() error {
|
|
if h == nil {
|
|
return nil
|
|
}
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
if !h.started {
|
|
return nil
|
|
}
|
|
h.started = false
|
|
h.grpcServer.Stop()
|
|
return h.listener.Close()
|
|
}
|
|
|
|
func (h *Host) SyncFromLifecycle() error {
|
|
if h == nil || h.lifecycle == nil || h.server == nil {
|
|
return nil
|
|
}
|
|
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
|
|
model, err := h.lifecycle.Current()
|
|
locked := false
|
|
switch {
|
|
case err == nil:
|
|
h.lastModel = model
|
|
case errors.Is(err, session.ErrLocked):
|
|
locked = true
|
|
default:
|
|
return err
|
|
}
|
|
|
|
dirty := false
|
|
if h.dirty != nil {
|
|
dirty = h.dirty()
|
|
}
|
|
h.server.SetSessionState(h.lastModel, locked, dirty)
|
|
return nil
|
|
}
|