internal/httpd/account.go

129 lines · 3496 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		Keys    []accountKey
 52		PGP     []accountPGP
 53		Emails  []store.Email
 54		Host    string
 55		Notice  string
 56		Message string
 57	}{s.baseFor(u), keys, pgp, emails, s.cfg.SiteHost(),
 58		r.URL.Query().Get("e"), r.URL.Query().Get("m")})
 59}
 60
 61// accountSubmit routes the account forms to their commands. Everything
 62// here is a public key or an address — no secret is accepted over the web.
 63func (s *Server) accountSubmit(w http.ResponseWriter, r *http.Request, u store.User) {
 64	back := func(msg, note string) {
 65		q := ""
 66		switch {
 67		case msg != "":
 68			q = "?e=" + url.QueryEscape(msg)
 69		case note != "":
 70			q = "?m=" + url.QueryEscape(note)
 71		}
 72		http.Redirect(w, r, "/settings"+q, http.StatusSeeOther)
 73	}
 74
 75	switch r.FormValue("field") {
 76	case "key-add":
 77		body := strings.TrimSpace(r.FormValue("key"))
 78		if body == "" {
 79			back("paste a public key in authorized_keys format", "")
 80			return
 81		}
 82		argv := []string{"keys", "add"}
 83		if scope := r.FormValue("scope"); scope == "git" {
 84			argv = append(argv, "--scope", "git")
 85		}
 86		if msg, ok := s.runControlStdin(u, argv, body+"\n"); !ok {
 87			back(msg, "")
 88			return
 89		}
 90		back("", "key registered")
 91	case "key-remove":
 92		if _, msg, ok := s.runControl(u, []string{"keys", "remove", r.FormValue("fingerprint")}); !ok {
 93			back(msg, "")
 94			return
 95		}
 96		back("", "key removed")
 97	case "pgp-add":
 98		body := strings.TrimSpace(r.FormValue("key"))
 99		if body == "" {
100			back("paste an armored OpenPGP public key", "")
101			return
102		}
103		if msg, ok := s.runControlStdin(u, []string{"pgp", "add"}, body+"\n"); !ok {
104			back(msg, "")
105			return
106		}
107		back("", "PGP key registered")
108	case "pgp-remove":
109		if _, msg, ok := s.runControl(u, []string{"pgp", "remove", r.FormValue("fingerprint")}); !ok {
110			back(msg, "")
111			return
112		}
113		back("", "PGP key removed")
114	case "email-add":
115		if _, msg, ok := s.runControl(u, []string{"email", "add", strings.TrimSpace(r.FormValue("address"))}); !ok {
116			back(msg, "")
117			return
118		}
119		back("", "check that inbox for a verification code")
120	case "email-verify":
121		if _, msg, ok := s.runControl(u, []string{"email", "verify", strings.TrimSpace(r.FormValue("code"))}); !ok {
122			back(msg, "")
123			return
124		}
125		back("", "address verified")
126	default:
127		back("unknown form", "")
128	}
129}