diff --git a/cmd/dkl-local-server/auth.go b/cmd/dkl-local-server/auth.go index 5afd882..0ad4db9 100644 --- a/cmd/dkl-local-server/auth.go +++ b/cmd/dkl-local-server/auth.go @@ -26,7 +26,7 @@ func authorizeToken(r *http.Request, token string) bool { } func forbidden(w http.ResponseWriter, r *http.Request) { - log.Printf("denied access to %s from %s", r.RequestURI, r.RemoteAddr) + log.Printf("denied access to %s from %s", r.URL.Path, r.RemoteAddr) http.Error(w, "Forbidden", http.StatusForbidden) } diff --git a/cmd/dkl-local-server/main.go b/cmd/dkl-local-server/main.go index af329a2..bf4692b 100644 --- a/cmd/dkl-local-server/main.go +++ b/cmd/dkl-local-server/main.go @@ -52,12 +52,12 @@ func main() { } if autoUnlock != "" { log.Printf("auto-unlocking the store") - err := unlockSecretStore([]byte(autoUnlock)) + err := unlockSecretStore("test", []byte(autoUnlock)) if err.Any() { log.Fatal(err) } - log.Print("store auto-unlocked, admin token is ", adminToken) + log.Print("store auto-unlocked, token is ", adminToken) } os.Setenv("DLS_AUTO_UNLOCK", "") diff --git a/cmd/dkl-local-server/secret-store.go b/cmd/dkl-local-server/secret-store.go index 94b7950..ebbf157 100644 --- a/cmd/dkl-local-server/secret-store.go +++ b/cmd/dkl-local-server/secret-store.go @@ -63,7 +63,7 @@ var ( ErrInvalidPassphrase = httperr.NewStd(2, http.StatusBadRequest, "invalid passphrase") ) -func unlockSecretStore(passphrase []byte) (err httperr.Error) { +func unlockSecretStore(name string, passphrase []byte) (err httperr.Error) { unlockMutex.Lock() defer unlockMutex.Unlock() @@ -72,7 +72,7 @@ func unlockSecretStore(passphrase []byte) (err httperr.Error) { } if secStore.IsNew() { - err := secStore.Init(passphrase) + err := secStore.Init(name, passphrase) if err != nil { return httperr.Internal(err) } diff --git a/cmd/dkl-local-server/state.go b/cmd/dkl-local-server/state.go index 4c4bbd9..e71542a 100644 --- a/cmd/dkl-local-server/state.go +++ b/cmd/dkl-local-server/state.go @@ -22,6 +22,7 @@ type State struct { Store struct { DownloadToken string + KeyNames []string } Clusters []ClusterState @@ -59,14 +60,21 @@ func init() { func updateState() { log.Print("updating state") + // store key names + keyNames := make([]string, 0, len(secStore.Keys)) + for _, key := range secStore.Keys { + keyNames = append(keyNames, key.Name) + } + + // config cfg, err := readConfig() if err != nil { - wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil }) + wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil; v.Store.KeyNames = keyNames }) return } if secStore.IsNew() || !secStore.Unlocked() { - wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil }) + wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil; v.Store.KeyNames = keyNames }) return } @@ -122,7 +130,7 @@ func updateState() { // done wState.Change(func(v *State) { v.HasConfig = true - //v.Config = cfg + v.Store.KeyNames = keyNames v.Clusters = clusters v.Hosts = hosts }) diff --git a/cmd/dkl-local-server/ws-public.go b/cmd/dkl-local-server/ws-public.go index 67e0576..e7e52f1 100644 --- a/cmd/dkl-local-server/ws-public.go +++ b/cmd/dkl-local-server/ws-public.go @@ -14,15 +14,32 @@ import ( "m.cluseau.fr/go/httperr" ) +type NamedPassphrase struct { + Name string + Passphrase []byte +} + func wsUnlockStore(req *restful.Request, resp *restful.Response) { - var passphrase string - err := req.ReadEntity(&passphrase) + np := NamedPassphrase{} + err := req.ReadEntity(&np) if err != nil { resp.WriteError(http.StatusBadRequest, err) return } - if err := unlockSecretStore([]byte(passphrase)); err.Any() { + 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 + } + + if err := unlockSecretStore(np.Name, np.Passphrase); err.Any() { err.WriteJSON(resp.ResponseWriter) return } diff --git a/cmd/dkl-local-server/ws-store.go b/cmd/dkl-local-server/ws-store.go index 12b4923..331b3ca 100644 --- a/cmd/dkl-local-server/ws-store.go +++ b/cmd/dkl-local-server/ws-store.go @@ -1,24 +1,77 @@ package main import ( + "strconv" + "strings" + restful "github.com/emicklei/go-restful" + "novit.tech/direktil/local-server/secretstore" ) func wsStoreAddKey(req *restful.Request, resp *restful.Response) { - var passphrase string + np := NamedPassphrase{} - err := req.ReadEntity(&passphrase) + err := req.ReadEntity(&np) if err != nil { wsBadRequest(resp, err.Error()) return } - if len(passphrase) == 0 { + np.Name = strings.TrimSpace(np.Name) + + if len(np.Name) == 0 { + wsBadRequest(resp, "no name given") + return + } + + if len(np.Passphrase) == 0 { wsBadRequest(resp, "no passphrase given") return } - secStore.AddKey([]byte(passphrase)) + secStore.AddKey(np.Name, np.Passphrase) + defer updateState() + + for _, k := range secStore.Keys { + if k.Name == np.Name { + wsBadRequest(resp, "there's already a passphrase named "+strconv.Quote(np.Name)) + return + } + } + + err = secStore.SaveTo(secKeysStorePath()) + if err != nil { + wsError(resp, err) + return + } +} + +func wsStoreDelKey(req *restful.Request, resp *restful.Response) { + name := "" + + err := req.ReadEntity(&name) + if err != nil { + wsBadRequest(resp, err.Error()) + return + } + + newKeys := make([]secretstore.KeyEntry, 0, len(secStore.Keys)) + for _, k := range secStore.Keys { + if k.Name == name { + continue + } + + newKeys = append(newKeys, k) + } + + if len(newKeys) == 0 { + wsBadRequest(resp, "can't remove the last key from the store") + return + } + + secStore.Keys = newKeys + defer updateState() + err = secStore.SaveTo(secKeysStorePath()) if err != nil { wsError(resp, err) diff --git a/cmd/dkl-local-server/ws.go b/cmd/dkl-local-server/ws.go index fb15aa8..a5cc172 100644 --- a/cmd/dkl-local-server/ws.go +++ b/cmd/dkl-local-server/ws.go @@ -27,7 +27,7 @@ func registerWS(rest *restful.Container) { Produces(mime.JSON). Consumes(mime.JSON). Route(ws.POST("/unlock-store").To(wsUnlockStore). - Reads(""). + Reads(NamedPassphrase{}). Writes(""). Doc("Try to unlock the store")). Route(ws.GET("/store.tar").To(wsStoreDownload). @@ -54,8 +54,11 @@ func registerWS(rest *restful.Container) { // - store management ws.Route(ws.POST("/store/add-key").To(wsStoreAddKey). - Consumes(mime.JSON).Reads(""). + Consumes(mime.JSON).Reads(NamedPassphrase{}). Doc("Add an unlock key to the store")) + ws.Route(ws.POST("/store/delete-key").To(wsStoreDelKey). + Consumes(mime.JSON).Reads(""). + Doc("Remove an unlock key to the store (by its name)")) // - downloads ws.Route(ws.POST("/authorize-download").To(wsAuthorizeDownload). diff --git a/html/ui/app.css b/html/ui/app.css index 8ed48a0..5695f73 100644 --- a/html/ui/app.css +++ b/html/ui/app.css @@ -21,21 +21,3 @@ .cluster { max-width: 50%; } - -#store-infos { - display: flex; - flex-flow: row wrap; - align-content: center; - justify-content: flex-start; - border-bottom: dashed 1pt; - margin-bottom: 1ex; -} -#store-infos > * { - display: block; - font-size: medium; - padding: 2pt 1ex; - margin: 0 0 0 1ex; -} -#store-infos > *:first-child { - margin-left: 0; -} diff --git a/html/ui/index.html b/html/ui/index.html index 5023130..06e8684 100644 --- a/html/ui/index.html +++ b/html/ui/index.html @@ -41,8 +41,9 @@
Store is new.
Option 1: initialize a new store
Option 2: upload a previously downloaded store
@@ -54,7 +55,7 @@Store is not open.
@@ -63,28 +64,12 @@Invalid token
- -{{ state }}+