Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion backend/cmd/seed-daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"flag"
"os"
"path/filepath"
"slices"
"strings"
"time"
Expand Down Expand Up @@ -89,7 +90,12 @@ func main() {
if keyStoreEnvironment == "" {
keyStoreEnvironment = "main"
}
ks := core.NewOSKeyStore(keyStoreEnvironment)
var ks core.KeyStore
if os.Getenv("SEED_FILE_KEYSTORE") == "1" {
ks = core.NewFileKeyStore(filepath.Join(cfg.Base.DataDir, "keys.json"))
} else {
ks = core.NewOSKeyStore(keyStoreEnvironment)
}

dir, err := storage.Open(cfg.Base.DataDir, nil, ks, cfg.LogLevel)
if err != nil {
Expand Down
155 changes: 155 additions & 0 deletions backend/core/file_keystore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package core

import (
"context"
"encoding/json"
"fmt"
"os"
"sync"
)

type fileKeyStore struct {
path string
mu sync.RWMutex
}

type fileKeyData struct {
Keys map[string][]byte `json:"keys"`
}

func NewFileKeyStore(path string) KeyStore {
return &fileKeyStore{path: path}
}

func (fks *fileKeyStore) load() (*fileKeyData, error) {
data, err := os.ReadFile(fks.path)
if err != nil {
if os.IsNotExist(err) {
return &fileKeyData{Keys: make(map[string][]byte)}, nil
}
return nil, err
}
var fkd fileKeyData
if err := json.Unmarshal(data, &fkd); err != nil {
return nil, err
}
if fkd.Keys == nil {
fkd.Keys = make(map[string][]byte)
}
return &fkd, nil
}

func (fks *fileKeyStore) save(fkd *fileKeyData) error {
data, err := json.MarshalIndent(fkd, "", " ")
if err != nil {
return err
}
return os.WriteFile(fks.path, data, 0600)
}

func (fks *fileKeyStore) GetKey(ctx context.Context, name string) (*KeyPair, error) {
fks.mu.RLock()
defer fks.mu.RUnlock()

fkd, err := fks.load()
if err != nil {
return nil, err
}

privBytes, ok := fkd.Keys[name]
if !ok {
return nil, fmt.Errorf("%s: %w", name, errKeyNotFound)
}

kp := new(KeyPair)
return kp, kp.UnmarshalBinary(privBytes)
}

func (fks *fileKeyStore) StoreKey(ctx context.Context, name string, kp *KeyPair) error {
if !nameFormat.MatchString(name) {
return fmt.Errorf("invalid name format")
}
if kp == nil {
return fmt.Errorf("can't store empty key")
}

fks.mu.Lock()
defer fks.mu.Unlock()

fkd, err := fks.load()
if err != nil {
return err
}

if _, ok := fkd.Keys[name]; ok {
return fmt.Errorf("Name already exists. Please delete it first")
}

keyBytes, err := kp.MarshalBinary()
if err != nil {
return err
}
fkd.Keys[name] = keyBytes
return fks.save(fkd)
}

func (fks *fileKeyStore) ListKeys(ctx context.Context) ([]NamedKey, error) {
fks.mu.RLock()
defer fks.mu.RUnlock()

fkd, err := fks.load()
if err != nil {
return nil, err
}

var ret []NamedKey
for name, privBytes := range fkd.Keys {
priv := new(KeyPair)
if err := priv.UnmarshalBinary(privBytes); err != nil {
return nil, err
}
ret = append(ret, NamedKey{Name: name, PublicKey: priv.Principal()})
}
return ret, nil
}

func (fks *fileKeyStore) DeleteKey(ctx context.Context, name string) error {
fks.mu.Lock()
defer fks.mu.Unlock()

fkd, err := fks.load()
if err != nil {
return err
}

if _, ok := fkd.Keys[name]; !ok {
return errKeyNotFound
}
delete(fkd.Keys, name)
return fks.save(fkd)
}

func (fks *fileKeyStore) DeleteAllKeys(ctx context.Context) error {
fks.mu.Lock()
defer fks.mu.Unlock()
return fks.save(&fileKeyData{Keys: make(map[string][]byte)})
}

func (fks *fileKeyStore) ChangeKeyName(ctx context.Context, currentName, newName string) error {
fks.mu.Lock()
defer fks.mu.Unlock()

fkd, err := fks.load()
if err != nil {
return err
}

privBytes, ok := fkd.Keys[currentName]
if !ok {
return errKeyNotFound
}

delete(fkd.Keys, currentName)
fkd.Keys[newName] = privBytes
return fks.save(fkd)
}
20 changes: 20 additions & 0 deletions frontend/apps/desktop/src/app-context-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export function AppContextProvider({
openMarkdownDirectories,
openLatexFiles,
openLatexDirectories,
openPdfFiles,
openPdfDirectories,
readMediaFile,
exportDocument,
exportDocuments,
Expand Down Expand Up @@ -62,6 +64,22 @@ export function AppContextProvider({
}[]
docMap: Map<string, {name: string; path: string}>
}>
openPdfFiles: (accountId: string) => Promise<{
documents: {
pdfContent: ArrayBuffer
title: string
directoryPath: string
}[]
docMap: Map<string, {name: string; path: string}>
}>
openPdfDirectories: (accountId: string) => Promise<{
documents: {
pdfContent: ArrayBuffer
title: string
directoryPath: string
}[]
docMap: Map<string, {name: string; path: string}>
}>
readMediaFile: (filePath: string) => Promise<{
filePath: string
content: string
Expand Down Expand Up @@ -99,6 +117,8 @@ export function AppContextProvider({
openMarkdownDirectories,
openLatexFiles,
openLatexDirectories,
openPdfFiles,
openPdfDirectories,
readMediaFile,
exportDocument,
exportDocuments,
Expand Down
16 changes: 16 additions & 0 deletions frontend/apps/desktop/src/app-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ export type AppContext = {
}[]
docMap: Map<string, {name: string; path: string}>
}>
openPdfFiles: (accountId: string) => Promise<{
documents: {
pdfContent: ArrayBuffer
title: string
directoryPath: string
}[]
docMap: Map<string, {name: string; path: string}>
}>
openPdfDirectories: (accountId: string) => Promise<{
documents: {
pdfContent: ArrayBuffer
title: string
directoryPath: string
}[]
docMap: Map<string, {name: string; path: string}>
}>
readMediaFile: (filePath: string) => Promise<{
filePath: string
content: string
Expand Down
2 changes: 2 additions & 0 deletions frontend/apps/desktop/src/components/create-doc-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ function ImportMenuItem({
onImportDirectory: importing.importDirectory,
onImportLatexFile: importing.importLatexFile,
onImportLatexDirectory: importing.importLatexDirectory,
onImportPdfFile: importing.importPdfFile,
onImportPdfDirectory: importing.importPdfDirectory,
})
}

Expand Down
Loading