diff options
author | Jan Dittberner <jandd@cacert.org> | 2017-04-21 11:31:32 +0200 |
---|---|---|
committer | Jan Dittberner <jan@dittberner.info> | 2017-04-22 00:14:11 +0200 |
commit | 8d1f18e16dcf44001155e8dff273f82900899d76 (patch) | |
tree | 6a7ed46e5ffb339cc316f9d862f6584d2aa82776 | |
parent | 2cac50ee86e70428aa06c99f5e009ceaaf1dbc13 (diff) | |
download | cacert-boardvoting-8d1f18e16dcf44001155e8dff273f82900899d76.tar.gz cacert-boardvoting-8d1f18e16dcf44001155e8dff273f82900899d76.tar.xz cacert-boardvoting-8d1f18e16dcf44001155e8dff273f82900899d76.zip |
Implement direct voting
-rw-r--r-- | boardvoting.go | 55 | ||||
-rw-r--r-- | notifications.go | 31 | ||||
-rw-r--r-- | templates/direct_vote_form.html | 22 | ||||
-rw-r--r-- | templates/direct_vote_mail.txt | 10 |
4 files changed, 106 insertions, 12 deletions
diff --git a/boardvoting.go b/boardvoting.go index d62b80e..e2e9838 100644 --- a/boardvoting.go +++ b/boardvoting.go @@ -53,6 +53,7 @@ const ( ctxVoter ctxDecision ctxVote + ctxAuthenticatedCert ) func authenticateRequest(w http.ResponseWriter, r *http.Request, handler func(http.ResponseWriter, *http.Request)) { @@ -66,7 +67,9 @@ func authenticateRequest(w http.ResponseWriter, r *http.Request, handler func(ht return } if voter != nil { - handler(w, r.WithContext(context.WithValue(r.Context(), ctxVoter, voter))) + requestContext := context.WithValue(r.Context(), ctxVoter, voter) + requestContext = context.WithValue(requestContext, ctxAuthenticatedCert, cert) + handler(w, r.WithContext(requestContext)) return } } @@ -475,12 +478,12 @@ func (h motionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -type voteHandler struct { +type directVoteHandler struct { FlashMessageAction authenticationRequiredHandler } -func (h *voteHandler) Handle(w http.ResponseWriter, r *http.Request) { +func (h *directVoteHandler) Handle(w http.ResponseWriter, r *http.Request) { decision, ok := getDecisionFromRequest(r) if !ok { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -496,10 +499,37 @@ func (h *voteHandler) Handle(w http.ResponseWriter, r *http.Request) { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } - fmt.Fprintln(w, "to be implemented") - fmt.Fprintln(w, "Decision:", decision) - fmt.Fprintln(w, "Voter:", voter) - fmt.Fprintln(w, "Vote:", vote) + switch r.Method { + case http.MethodPost: + voteResult := &Vote{ + VoterId: voter.Id, Vote: vote, DecisionId: decision.Id, Voted: time.Now().UTC(), + Notes: fmt.Sprintf("Direct Vote\n\n%s", getPEMClientCert(r))} + if err := voteResult.Save(); err != nil { + logger.Println("ERROR", "Problem saving vote:", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + NotifyMailChannel <- NewNotificationDirectVote(&decision.Decision, voter, voteResult) + + if err := h.AddFlash(w, r, "Your vote has been registered."); err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/motions/", http.StatusMovedPermanently) + default: + templates := []string{"direct_vote_form.html", "header.html", "footer.html", "motion_fragments.html"} + var templateContext struct { + Decision *DecisionForDisplay + VoteChoice VoteChoice + PageTitle string + Flashes interface{} + } + templateContext.Decision = decision + templateContext.VoteChoice = vote + renderTemplate(w, templates, templateContext) + } } type proxyVoteHandler struct { @@ -509,7 +539,8 @@ type proxyVoteHandler struct { func getPEMClientCert(r *http.Request) string { clientCertPEM := bytes.NewBufferString("") - pem.Encode(clientCertPEM, &pem.Block{Type: "CERTIFICATE", Bytes: r.TLS.PeerCertificates[0].Raw}) + authenticatedCertificate := r.Context().Value(ctxAuthenticatedCert).(*x509.Certificate) + pem.Encode(clientCertPEM, &pem.Block{Type: "CERTIFICATE", Bytes: authenticatedCertificate.Raw}) return clientCertPEM.String() } @@ -604,13 +635,17 @@ func (h *decisionVoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) }) case strings.HasPrefix(r.URL.Path, "/vote/"): parts := strings.Split(r.URL.Path[len("/vote/"):], "/") + if len(parts) != 2 { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } motionTag := parts[0] voteValue, ok := VoteValues[parts[1]] if !ok { - http.NotFound(w, r) + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } - handler := &voteHandler{} + handler := &directVoteHandler{} authenticateRequest( w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, true)), func(w http.ResponseWriter, r *http.Request) { diff --git a/notifications.go b/notifications.go index 499a689..4a76710 100644 --- a/notifications.go +++ b/notifications.go @@ -113,7 +113,7 @@ type NotificationCreateMotion struct { } func (n *NotificationCreateMotion) GetData() interface{} { - voteURL := fmt.Sprintf("%s/vote", config.BaseURL) + voteURL := fmt.Sprintf("%s/vote/%s", config.BaseURL, n.decision.Tag) unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL) return struct { *Decision @@ -146,7 +146,7 @@ func NewNotificationUpdateMotion(decision Decision, voter Voter) *NotificationUp } func (n *NotificationUpdateMotion) GetData() interface{} { - voteURL := fmt.Sprintf("%s/vote", config.BaseURL) + voteURL := fmt.Sprintf("%s/vote/%s", config.BaseURL, n.decision.Tag) unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL) return struct { *Decision @@ -250,3 +250,30 @@ func (n *NotificationProxyVote) GetTemplate() string { return "proxy_vote_mail.t func (n *NotificationProxyVote) GetSubject() string { return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title) } + +type NotificationDirectVote struct { + voteNotificationBase + decisionReplyBase + voter Voter + vote Vote +} + +func NewNotificationDirectVote(decision *Decision, voter *Voter, vote *Vote) *NotificationDirectVote { + notification := &NotificationDirectVote{voter: *voter, vote: *vote} + notification.decision = *decision + return notification +} + +func (n *NotificationDirectVote) GetData() interface{} { + return struct { + Vote VoteChoice + Voter string + Decision *Decision + }{n.vote.Vote, n.voter.Name, &n.decision} +} + +func (n *NotificationDirectVote) GetTemplate() string { return "direct_vote_mail.txt" } + +func (n *NotificationDirectVote) GetSubject() string { + return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title) +} diff --git a/templates/direct_vote_form.html b/templates/direct_vote_form.html new file mode 100644 index 0000000..3b656f2 --- /dev/null +++ b/templates/direct_vote_form.html @@ -0,0 +1,22 @@ +{{ template "header" . }} +<a href="/motions/">Show all votes</a> +<table class="list"> + <thead> + <tr> + <th>Status</th> + <th>Motion</th> + </tr> + </thead> + <tbody> + <tr> + {{ with .Decision }} + {{ template "motion_fragment" .}} + {{ end}} + </tr> + </tbody> +</table> + +<form action="/vote/{{ .Decision.Tag }}/{{ .VoteChoice }}" method="post"> + <input type="submit" value="Vote {{ .VoteChoice }}"/> +</form> +{{ template "footer" . }}
\ No newline at end of file diff --git a/templates/direct_vote_mail.txt b/templates/direct_vote_mail.txt new file mode 100644 index 0000000..95bacb8 --- /dev/null +++ b/templates/direct_vote_mail.txt @@ -0,0 +1,10 @@ +Dear Board, + +{{ .Voter }} has just voted {{ .Vote }} on motion {{ .Decision.Tag }}. + +Motion: + {{ .Decision.Title }} + {{ .Decision.Content }} + +Kind regards, +the vote system
\ No newline at end of file |