internal/httpd/account.go

6b2b26db548cdb89367154ba6ff4b0bcce21dd67
gitbay/internal/httpd/account.go history · blame · raw

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}