2023-02-07 20:29:19 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2023-02-13 12:03:42 +00:00
|
|
|
"archive/tar"
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
2023-08-19 19:17:23 +00:00
|
|
|
"log"
|
2023-02-07 20:29:19 +00:00
|
|
|
"net/http"
|
2023-02-13 12:03:42 +00:00
|
|
|
"os"
|
2023-08-19 19:17:23 +00:00
|
|
|
"path/filepath"
|
2023-02-07 20:29:19 +00:00
|
|
|
|
|
|
|
restful "github.com/emicklei/go-restful"
|
2023-08-19 19:17:23 +00:00
|
|
|
"m.cluseau.fr/go/httperr"
|
2023-11-09 07:59:27 +00:00
|
|
|
"novit.tech/direktil/local-server/secretstore"
|
2023-02-07 20:29:19 +00:00
|
|
|
)
|
|
|
|
|
2023-09-10 14:47:54 +00:00
|
|
|
type NamedPassphrase struct {
|
|
|
|
Name string
|
|
|
|
Passphrase []byte
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:29:19 +00:00
|
|
|
func wsUnlockStore(req *restful.Request, resp *restful.Response) {
|
2023-09-10 14:47:54 +00:00
|
|
|
np := NamedPassphrase{}
|
|
|
|
err := req.ReadEntity(&np)
|
2023-02-07 20:29:19 +00:00
|
|
|
if err != nil {
|
|
|
|
resp.WriteError(http.StatusBadRequest, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-11-09 07:59:27 +00:00
|
|
|
defer secretstore.Memzero(np.Passphrase)
|
|
|
|
|
2023-09-10 14:47:54 +00:00
|
|
|
if secStore.IsNew() {
|
|
|
|
if len(np.Name) == 0 {
|
|
|
|
wsBadRequest(resp, "no name given")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(np.Passphrase) == 0 {
|
|
|
|
wsBadRequest(resp, "no passphrase given")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-11-09 07:59:27 +00:00
|
|
|
if secStore.Unlocked() {
|
|
|
|
if secStore.HasKey(np.Passphrase) {
|
|
|
|
resp.WriteEntity(adminToken)
|
|
|
|
} else {
|
|
|
|
wsError(resp, ErrUnauthorized)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-10 14:47:54 +00:00
|
|
|
if err := unlockSecretStore(np.Name, np.Passphrase); err.Any() {
|
2023-02-07 20:29:19 +00:00
|
|
|
err.WriteJSON(resp.ResponseWriter)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-18 17:55:52 +00:00
|
|
|
resp.WriteEntity(adminToken)
|
2023-02-07 20:29:19 +00:00
|
|
|
}
|
2023-02-13 12:03:42 +00:00
|
|
|
|
|
|
|
func wsStoreDownload(req *restful.Request, resp *restful.Response) {
|
|
|
|
token := req.QueryParameter("token")
|
|
|
|
if token != wState.Get().Store.DownloadToken {
|
|
|
|
wsError(resp, ErrInvalidToken)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
arch := tar.NewWriter(buf)
|
|
|
|
|
|
|
|
root := os.DirFS(secStoreRoot())
|
|
|
|
|
|
|
|
err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, readErr error) (err error) {
|
|
|
|
if readErr != nil {
|
|
|
|
err = readErr
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if path == "." {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fi, err := d.Info()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr, err := tar.FileInfoHeader(fi, "")
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr.Name = path
|
|
|
|
hdr.Uid = 0
|
|
|
|
hdr.Gid = 0
|
|
|
|
|
|
|
|
err = arch.WriteHeader(hdr)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if fi.IsDir() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := root.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
io.Copy(arch, f)
|
|
|
|
|
|
|
|
return
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = arch.Close()
|
|
|
|
if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteTo(resp)
|
|
|
|
}
|
2023-08-19 19:17:23 +00:00
|
|
|
|
|
|
|
func wsStoreUpload(req *restful.Request, resp *restful.Response) {
|
|
|
|
if !secStore.IsNew() {
|
|
|
|
wsError(resp, httperr.BadRequest("store is not new"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
|
|
|
|
_, err := io.Copy(buf, req.Request.Body)
|
|
|
|
if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
arch := tar.NewReader(buf)
|
|
|
|
|
|
|
|
root := secStoreRoot()
|
|
|
|
|
|
|
|
for {
|
|
|
|
hdr, err := arch.Next()
|
|
|
|
if err == io.EOF {
|
|
|
|
err = nil
|
|
|
|
break
|
|
|
|
} else if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Print(hdr.Name)
|
|
|
|
|
|
|
|
fullPath := filepath.Join(root, hdr.Name)
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case hdr.FileInfo().IsDir():
|
|
|
|
err = os.MkdirAll(fullPath, 0700)
|
|
|
|
if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
content, err := io.ReadAll(io.LimitReader(arch, hdr.Size))
|
|
|
|
if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = os.WriteFile(fullPath, content, 0600)
|
|
|
|
if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
wsError(resp, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
openSecretStore()
|
|
|
|
|
|
|
|
resp.WriteEntity(map[string]any{"ok": true})
|
|
|
|
}
|