internal/httpd/account.go
143 lines · 3820 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 Comment string
19}
20
21type accountPGP struct {
22 Fingerprint string
23 UIDs []string
24 Expired bool
25 Revoked bool
26}
27
28// accountForm renders the account's own settings: keys, addresses, and the
29// commands for everything that stays on SSH.
30func (s *Server) accountForm(w http.ResponseWriter, r *http.Request, u store.User) {
31 var keys []accountKey
32 if list, err := s.st.ListSSHKeys(u.ID); err == nil {
33 for _, k := range list {
34 keys = append(keys, accountKey{
35 Fingerprint: k.Fingerprint, Algo: k.Algo, Scope: k.Scope,
36 Comment: keyComment(k.Blob),
37 })
38 }
39 }
40 var pgp []accountPGP
41 if list, err := s.st.ListPGPKeys(u.ID); err == nil {
42 for _, k := range list {
43 var uids []string
44 json.Unmarshal([]byte(k.UIDsJSON), &uids)
45 pgp = append(pgp, accountPGP{
46 Fingerprint: k.Fingerprint, UIDs: uids,
47 Expired: k.ExpiresAt != nil, Revoked: k.RevokedAt != nil,
48 })
49 }
50 }
51 emails, _ := s.st.ListEmails(u.ID)
52
53 s.render(w, "account.html", struct {
54 basePage
55 Keys []accountKey
56 PGP []accountPGP
57 Emails []store.Email
58 Host string
59 Notice string
60 Message string
61 }{s.baseFor(u), keys, pgp, emails, s.cfg.SiteHost(),
62 r.URL.Query().Get("e"), r.URL.Query().Get("m")})
63}
64
65// keyComment pulls the trailing comment off an authorized_keys blob, which
66// is how people tell their own keys apart.
67func keyComment(blob []byte) string {
68 f := strings.Fields(string(blob))
69 if len(f) < 3 {
70 return ""
71 }
72 return strings.Join(f[2:], " ")
73}
74
75// accountSubmit routes the account forms to their commands. Everything
76// here is a public key or an address — no secret is accepted over the web.
77func (s *Server) accountSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
78 back := func(msg, note string) {
79 q := ""
80 switch {
81 case msg != "":
82 q = "?e=" + url.QueryEscape(msg)
83 case note != "":
84 q = "?m=" + url.QueryEscape(note)
85 }
86 http.Redirect(w, r, "/settings"+q, http.StatusSeeOther)
87 }
88
89 switch r.FormValue("field") {
90 case "key-add":
91 body := strings.TrimSpace(r.FormValue("key"))
92 if body == "" {
93 back("paste a public key in authorized_keys format", "")
94 return
95 }
96 argv := []string{"keys", "add"}
97 if scope := r.FormValue("scope"); scope == "git" {
98 argv = append(argv, "--scope", "git")
99 }
100 if msg, ok := s.runControlStdin(u, argv, body+"\n"); !ok {
101 back(msg, "")
102 return
103 }
104 back("", "key registered")
105 case "key-remove":
106 if _, msg, ok := s.runControl(u, []string{"keys", "remove", r.FormValue("fingerprint")}); !ok {
107 back(msg, "")
108 return
109 }
110 back("", "key removed")
111 case "pgp-add":
112 body := strings.TrimSpace(r.FormValue("key"))
113 if body == "" {
114 back("paste an armored OpenPGP public key", "")
115 return
116 }
117 if msg, ok := s.runControlStdin(u, []string{"pgp", "add"}, body+"\n"); !ok {
118 back(msg, "")
119 return
120 }
121 back("", "PGP key registered")
122 case "pgp-remove":
123 if _, msg, ok := s.runControl(u, []string{"pgp", "remove", r.FormValue("fingerprint")}); !ok {
124 back(msg, "")
125 return
126 }
127 back("", "PGP key removed")
128 case "email-add":
129 if _, msg, ok := s.runControl(u, []string{"email", "add", strings.TrimSpace(r.FormValue("address"))}); !ok {
130 back(msg, "")
131 return
132 }
133 back("", "check that inbox for a verification code")
134 case "email-verify":
135 if _, msg, ok := s.runControl(u, []string{"email", "verify", strings.TrimSpace(r.FormValue("code"))}); !ok {
136 back(msg, "")
137 return
138 }
139 back("", "address verified")
140 default:
141 back("unknown form", "")
142 }
143}