internal/httpd/account.go
128 lines · 3518 bytes
1package httpd
2
3import (
4 "encoding/json"
5 "net/http"
6 "net/url"
7 "strings"
8
9 "gitbay.org/gitbay/internal/store"
10)
11
12// accountKey is one SSH key as the settings page shows it: enough to
13// recognise which key this is without printing the whole blob.
14type accountKey struct {
15 Fingerprint string
16 Algo string
17 Scope string
18}
19
20type accountPGP struct {
21 Fingerprint string
22 UIDs []string
23 Expired bool
24 Revoked bool
25}
26
27// accountForm renders the account's own settings: keys, addresses, and the
28// commands for everything that stays on SSH.
29func (s *Server) accountForm(w http.ResponseWriter, r *http.Request, u store.User) {
30 var keys []accountKey
31 if list, err := s.st.ListSSHKeys(u.ID); err == nil {
32 for _, k := range list {
33 keys = append(keys, accountKey{Fingerprint: k.Fingerprint, Algo: k.Algo, Scope: k.Scope})
34 }
35 }
36 var pgp []accountPGP
37 if list, err := s.st.ListPGPKeys(u.ID); err == nil {
38 for _, k := range list {
39 var uids []string
40 json.Unmarshal([]byte(k.UIDsJSON), &uids)
41 pgp = append(pgp, accountPGP{
42 Fingerprint: k.Fingerprint, UIDs: uids,
43 Expired: k.ExpiresAt != nil, Revoked: k.RevokedAt != nil,
44 })
45 }
46 }
47 emails, _ := s.st.ListEmails(u.ID)
48
49 s.render(w, "account.html", struct {
50 basePage
51 Tab string // marks the rail's Settings row as current
52 Keys []accountKey
53 PGP []accountPGP
54 Emails []store.Email
55 Host string
56 Notice string
57 Message string
58 }{s.baseFor(u), "account", keys, pgp, emails, s.cfg.SiteHost(),
59 s.takeFlash(w, r), r.URL.Query().Get("m")})
60}
61
62// accountSubmit routes the account forms to their commands. Everything
63// here is a public key or an address — no secret is accepted over the web.
64func (s *Server) accountSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
65 back := func(msg, note string) {
66 q := ""
67 if note != "" {
68 q = "?m=" + url.QueryEscape(note)
69 }
70 s.setFlash(w, msg)
71 http.Redirect(w, r, "/settings"+q, http.StatusSeeOther)
72 }
73
74 switch r.FormValue("field") {
75 case "key-add":
76 body := strings.TrimSpace(r.FormValue("key"))
77 if body == "" {
78 back("paste a public key in authorized_keys format", "")
79 return
80 }
81 argv := []string{"keys", "add"}
82 if scope := r.FormValue("scope"); scope == "git" {
83 argv = append(argv, "--scope", "git")
84 }
85 if msg, ok := s.runControlStdin(u, argv, body+"\n"); !ok {
86 back(msg, "")
87 return
88 }
89 back("", "key registered")
90 case "key-remove":
91 if _, msg, ok := s.runControl(u, []string{"keys", "remove", r.FormValue("fingerprint")}); !ok {
92 back(msg, "")
93 return
94 }
95 back("", "key removed")
96 case "pgp-add":
97 body := strings.TrimSpace(r.FormValue("key"))
98 if body == "" {
99 back("paste an armored OpenPGP public key", "")
100 return
101 }
102 if msg, ok := s.runControlStdin(u, []string{"pgp", "add"}, body+"\n"); !ok {
103 back(msg, "")
104 return
105 }
106 back("", "PGP key registered")
107 case "pgp-remove":
108 if _, msg, ok := s.runControl(u, []string{"pgp", "remove", r.FormValue("fingerprint")}); !ok {
109 back(msg, "")
110 return
111 }
112 back("", "PGP key removed")
113 case "email-add":
114 if _, msg, ok := s.runControl(u, []string{"email", "add", strings.TrimSpace(r.FormValue("address"))}); !ok {
115 back(msg, "")
116 return
117 }
118 back("", "check that inbox for a verification code")
119 case "email-verify":
120 if _, msg, ok := s.runControl(u, []string{"email", "verify", strings.TrimSpace(r.FormValue("code"))}); !ok {
121 back(msg, "")
122 return
123 }
124 back("", "address verified")
125 default:
126 back("unknown form", "")
127 }
128}