coverage~bigbes/sr-ht-dolt3523280cweb/handlers_keys.go

Coverage
60.0% 27/45 statements
Δ
+0.0
Blob
03adf01
1 package web
2
3 import (
4 "errors"
5 "net/http"
6 "net/url"
7 "strconv"
8 "strings"
9
10 "sourcecraft.dev/bigbes/sr-ht-ecore/chrome"
11 "sourcecraft.dev/bigbes/sr-ht-ecore/pages"
12
13 "sourcecraft.dev/bigbes/sr-ht-dolt/db"
14 )
15
16 // keysView is the dolt-key management page model.
17 type keysView struct {
18 chrome.Page
19 Keys []*db.DoltKey
20 Error string
21 Notice string
22 }
23
24 4 func (a *app) renderKeys(w http.ResponseWriter, r *http.Request, ac *authContext, status int, errMsg, notice string) {
25 4 keys, err := a.cfg.Repos.ListKeysByUser(r.Context(), ac.UserID)
26 4 if err != nil {
27 0 http.Error(w, "failed to list keys", http.StatusInternalServerError)
28 0 return
29 0 }
30 4 view := keysView{
31 4 Page: a.page(r, "Dolt keys — "+serviceName),
32 4 Keys: keys,
33 4 Error: errMsg,
34 4 Notice: notice,
35 4 }
36 4 a.render(w, status, "keys", view)
37 }
38
39 // handleKeys renders the dolt-key page: the user's registered keys and the
40 // add-key form. The page reads a `#<pubkey-base32>` URL fragment (which
41 // `dolt login` appends) into the form via a few lines of inline JS, but works
42 // without JS too — the user can paste the key the CLI printed. Login required.
43 1 func (a *app) handleKeys(w http.ResponseWriter, r *http.Request) {
44 1 ac := a.requireLogin(w, r)
45 1 if ac == nil {
46 0 return
47 0 }
48 1 a.renderKeys(w, r, ac, http.StatusOK, "", "")
49 }
50
51 // handleKeysPost adds or deletes a dolt key. A form carrying `delete_id` removes
52 // that key; otherwise `pubkey` (the base32 string dolt emits) is decoded,
53 // validated and registered. Login is required; the same-origin check is the
54 // router's (csrf.Require) and has already run.
55 3 func (a *app) handleKeysPost(w http.ResponseWriter, r *http.Request) {
56 3 ac := a.requireLogin(w, r)
57 3 if ac == nil {
58 0 return
59 0 }
60 3 form, err := pages.FormValues(w, r, 0)
61 3 if err != nil {
62 0 a.renderKeys(w, r, ac, http.StatusBadRequest, "Malformed form submission.", "")
63 0 return
64 0 }
65
66 3 if idStr := form.Get("delete_id"); idStr != "" {
67 1 a.keysDelete(w, r, ac, idStr)
68 1 return
69 1 }
70 2 a.keysAdd(w, r, ac, form)
71 }
72
73 // keysAdd decodes and registers a dolt public key for the caller.
74 2 func (a *app) keysAdd(w http.ResponseWriter, r *http.Request, ac *authContext, form url.Values) {
75 2 pubStr := strings.TrimSpace(form.Get("pubkey"))
76 2 comment := strings.TrimSpace(form.Get("comment"))
77 2
78 2 pubkey, kid, err := decodeDoltPubKey(pubStr)
79 2 if err != nil {
80 1 a.renderKeys(w, r, ac, http.StatusBadRequest, "Invalid public key: "+err.Error(), "")
81 1 return
82 1 }
83
84 1 if _, err := a.cfg.Repos.InsertKey(r.Context(), ac.UserID, kid, pubkey, comment); err != nil {
85 0 if errors.Is(err, db.ErrKeyExists) {
86 0 a.renderKeys(w, r, ac, http.StatusConflict, "That key is already registered.", "")
87 0 return
88 0 }
89 0 http.Error(w, "failed to register key", http.StatusInternalServerError)
90 0 return
91 }
92 1 a.renderKeys(w, r, ac, http.StatusOK, "", "Key added. You can now use `dolt clone`/`push` without --user.")
93 }
94
95 // keysDelete removes one of the caller's keys, scoped by user id so a user can
96 // only delete their own keys.
97 1 func (a *app) keysDelete(w http.ResponseWriter, r *http.Request, ac *authContext, idStr string) {
98 1 id, err := strconv.Atoi(idStr)
99 1 if err != nil {
100 0 a.renderKeys(w, r, ac, http.StatusBadRequest, "Invalid key id.", "")
101 0 return
102 0 }
103 1 if err := a.cfg.Repos.DeleteKey(r.Context(), id, ac.UserID); err != nil {
104 0 if errors.Is(err, db.ErrNotFound) {
105 0 a.renderKeys(w, r, ac, http.StatusNotFound, "No such key.", "")
106 0 return
107 0 }
108 0 http.Error(w, "failed to delete key", http.StatusInternalServerError)
109 0 return
110 }
111 1 a.renderKeys(w, r, ac, http.StatusOK, "", "Key deleted.")
112 }