Compare commits

..

12 commits
0.8.0 ... main

Author SHA1 Message Date
e327e5e2ee Fix name of message_id_domain in config example
All checks were successful
cacert-boardvoting/pipeline/head This commit looks good
2024-09-21 11:25:38 +02:00
39393eb612 Adapt goreleaser config for gorelease v2.0.0
All checks were successful
cacert-boardvoting/pipeline/head This commit looks good
2024-06-08 12:51:34 +02:00
a8f16192a8 Update dependencies 2024-06-08 12:51:34 +02:00
11582d3590 Update linter config and apply suggestions
- remove copyright years (they are in git)
- remove outdated linter bug workarounds
- update .golangci.yml to match current schema (as of golangci-lint 1.59.0)
2024-06-08 12:51:34 +02:00
3d16034c44 Add configurable domain part for message ids
Fixes #1
2024-06-08 12:51:14 +02:00
20d324f5cb Fix linter error
All checks were successful
cacert-boardvoting/pipeline/head This commit looks good
- remove nextPotentialRun.Add that had no effect
2023-05-12 19:18:15 +02:00
4276594f8d Bump copyright year
Some checks failed
cacert-boardvoting/pipeline/head There was a failure building this commit
2023-05-12 17:56:45 +02:00
4a8307e16a Fix display of user 2023-05-12 17:56:17 +02:00
12796486d2 Fix summarizing vote results 2023-05-12 17:51:42 +02:00
f27f2bf801 Update to go 1.19
- update go tool in Jenkinsfile
- update go version in go.mod
- update README.md
- update dependencies
2023-05-12 17:37:04 +02:00
d0052ff3dc Import go-sqlite3 to fix test
All checks were successful
cacert-boardvoting/pipeline/head This commit looks good
2022-10-16 12:05:31 +02:00
c9d3f2a20a Fix permission issues for unauthenticated users
All checks were successful
cacert-boardvoting/pipeline/head This commit looks good
2022-10-16 11:37:51 +02:00
33 changed files with 175 additions and 1383 deletions

View file

@ -1,7 +1,9 @@
---
run:
go: "1.17"
skip-files:
go: "1.22"
issues:
exclude-files:
- boardvoting/assets.go
output:
@ -13,7 +15,7 @@ linters-settings:
const:
ORGANIZATION: CAcert Inc.
template: |-
Copyright {{ YEAR-RANGE }} {{ ORGANIZATION }}
Copyright {{ ORGANIZATION }}
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -27,8 +29,8 @@ linters-settings:
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
gomnd:
ignore-functions:
mnd:
ignored-functions:
- 'strconv.*'
ignored-numbers:
- '-1,0,1,2,8'
@ -57,7 +59,7 @@ linters:
- gofmt
- goheader
- goimports
- gomnd
- mnd
- gosec
- lll
- makezero

View file

@ -1,6 +1,7 @@
# This is an example .goreleaser.yml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
project_name: cacert-boardvoting
version: 2
before:
hooks:
- go mod tidy
@ -17,11 +18,6 @@ builds:
- -trimpath
- -v
main: ./cmd/boardvoting
archives:
- replacements:
linux: Linux
amd64: x86_64
format: binary
checksum:
name_template: 'checksums.txt'
snapshot:

4
Jenkinsfile vendored
View file

@ -1,6 +1,6 @@
#!groovy
/*
Copyright 2017-2022 Jan Dittberner
Copyright 2017-2023 Jan Dittberner
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this program except in compliance with the License.
@ -18,7 +18,7 @@ pipeline {
agent any
tools {
go "go-1.18"
go "go-1.19"
}
environment {

View file

@ -53,7 +53,7 @@ Last Changed Date: 2009-07-12 04:02:38 +0000 (Sun, 12 Jul 2009)
Local development requires
* golang >= 1.18
* golang >= 1.19
* sqlite3 and development headers
* GNU make
* nodejs, npm and gulp (only needed if you intend to update the [jQuery] or [Fomantic-UI] CSS and JavaScript)

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -7,6 +7,7 @@ mail_config:
smtp_host: localhost
smtp_port: 25
base_url: https://motions.cacert.org
message_id_domain: motions.cacert.org
notice_mail_address: cacert-board@lists.cacert.org
vote_notice_mail_address: cacert-board-votes@lists.cacert.org
notification_sender_address: returns@cacert.org

44
go.mod
View file

@ -1,46 +1,46 @@
module git.cacert.org/cacert-boardvoting
go 1.18
go 1.22
require (
github.com/Masterminds/sprig/v3 v3.2.2
github.com/golang-migrate/migrate/v4 v4.15.2
github.com/google/uuid v1.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3
github.com/golang-migrate/migrate/v4 v4.17.1
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/jmoiron/sqlx v1.3.5
github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jmoiron/sqlx v1.4.0
github.com/johejo/golang-migrate-extra v0.0.0-20211005021153-c17dd75f8b4a
github.com/mattn/go-sqlite3 v1.14.12
github.com/mattn/go-sqlite3 v1.14.22
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/vearutop/statigz v1.1.8
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 // indirect
github.com/vearutop/statigz v1.4.0
golang.org/x/crypto v0.24.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/mail.v2 v2.3.1
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/alexedwards/scs/sqlite3store v0.0.0-20220216073957-c252878bcf5a
github.com/alexedwards/scs/v2 v2.5.0
github.com/go-chi/chi/v5 v5.0.7
github.com/go-playground/form/v4 v4.2.0
github.com/alexedwards/scs/sqlite3store v0.0.0-20240316134038-7e11d57e8885
github.com/alexedwards/scs/v2 v2.8.0
github.com/go-chi/chi/v5 v5.0.12
github.com/go-playground/form/v4 v4.2.1
github.com/justinas/nosurf v1.1.1
github.com/lestrrat-go/tcputil v0.0.0-20180223003554-d3c7f98154fb
github.com/stretchr/testify v1.7.0
golang.org/x/text v0.3.7
github.com/stretchr/testify v1.8.3
golang.org/x/text v0.16.0
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.4.1 // indirect
go.uber.org/atomic v1.9.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

1364
go.sum

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -199,12 +199,10 @@ func (app *Application) Routes() http.Handler {
r.Get("/newmotion/", motionHandler.NewForm)
r.Post("/newmotion/", motionHandler.New)
r.Route("/motions/{tag}", func(r chi.Router) {
r.Get("/edit", motionHandler.EditForm)
r.Post("/edit", motionHandler.Edit)
r.Get("/withdraw", motionHandler.WithdrawForm)
r.Post("/withdraw", motionHandler.Withdraw)
})
r.Get("/motions/{tag}/edit", motionHandler.EditForm)
r.Post("/motions/{tag}/edit", motionHandler.Edit)
r.Get("/motions/{tag}/withdraw", motionHandler.WithdrawForm)
r.Post("/motions/{tag}/withdraw", motionHandler.Withdraw)
})
r.Group(func(r chi.Router) {

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -33,6 +33,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
_ "github.com/mattn/go-sqlite3"
"git.cacert.org/cacert-boardvoting/internal/notifications"
"git.cacert.org/cacert-boardvoting/internal/models"

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -62,7 +62,7 @@ func Test_secureHeaders(t *testing.T) {
SecureHeaders(next).ServeHTTP(rr, r)
rs := rr.Result() //nolint:bodyclose // linters bug
rs := rr.Result()
defer func() { _ = rs.Body.Close() }()
@ -119,7 +119,7 @@ func TestApplication_tryAuthenticate(t *testing.T) {
mw.TryAuthenticate(next).ServeHTTP(rr, r)
rs := rr.Result() //nolint:bodyclose // linters bug
rs := rr.Result()
defer func() { _ = rs.Body.Close() }()
@ -137,7 +137,7 @@ func TestApplication_tryAuthenticate(t *testing.T) {
mw.TryAuthenticate(next).ServeHTTP(rr, r)
rs := rr.Result() //nolint:bodyclose // linters bug
rs := rr.Result()
defer func() { _ = rs.Body.Close() }()
@ -158,11 +158,12 @@ func TestApplication_tryAuthenticate(t *testing.T) {
mw.TryAuthenticate(next).ServeHTTP(rr, r)
rs := rr.Result() //nolint:bodyclose // linters bug
rs := rr.Result()
defer func() { _ = rs.Body.Close() }()
assert.Equal(t, http.StatusOK, rs.StatusCode)
user := nextCtx.Value(ctxUser)
assert.NotNil(t, user)

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -34,6 +34,10 @@ import (
)
func checkRole(v *models.User, roles ...models.RoleName) (bool, error) {
if v == nil {
return false, nil
}
hasRole, err := v.HasRole(roles...)
if err != nil {
return false, fmt.Errorf("could not determine user roles: %w", err)

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -47,7 +47,6 @@ func (r *RemindVotersJob) Schedule() {
year, month, day := now.Date()
nextPotentialRun := time.Date(year, month, day+1, 0, 0, 0, 0, time.UTC)
nextPotentialRun.Add(hoursInDay * time.Hour)
relevantDue := nextPotentialRun.Add(reminderDays * hoursInDay * time.Hour)

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -458,13 +458,15 @@ func sumsForDecision(ctx context.Context, tx *sqlx.Tx, d *Motion) (*VoteSums, er
return nil, fmt.Errorf("could not parse row for vote sums of motion %s: %w", d.Tag, err)
}
switch vote {
case VoteAye:
switch vote.ID {
case VoteAye.ID:
sums.Ayes = count
case VoteNaye:
case VoteNaye.ID:
sums.Nayes = count
case VoteAbstain:
case VoteAbstain.ID:
sums.Abstains = count
default:
return nil, fmt.Errorf("unknown vote type '%+v'", vote)
}
}
@ -605,12 +607,12 @@ func (m *MotionModel) List(ctx context.Context, options *MotionListOptions) ([]*
}
func (m *MotionModel) FillVoteSums(ctx context.Context, decisions []*Motion) error {
decisionIds := make([]int64, len(decisions))
decisionIDs := make([]int64, len(decisions))
decisionMap := make(map[int64]*Motion, len(decisions))
for idx, decision := range decisions {
decision.Sums = &VoteSums{}
decisionIds[idx] = decision.ID
decisionIDs[idx] = decision.ID
decisionMap[decision.ID] = decision
}
@ -619,7 +621,7 @@ func (m *MotionModel) FillVoteSums(ctx context.Context, decisions []*Motion) err
FROM votes v
WHERE v.decision IN (?)
GROUP BY v.decision, v.vote`,
decisionIds,
decisionIDs,
)
if err != nil {
return fmt.Errorf("could not create IN query: %w", err)

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -251,6 +251,10 @@ WHERE e.address IN (?)`, emails)
}
}
if count == 0 {
return nil, nil
}
if user.roles, err = m.Roles(ctx, &user); err != nil {
return nil, fmt.Errorf("could not retrieve roles for user %s: %w", user.Name, err)
}

View file

@ -1,5 +1,5 @@
/*
Copyright 2017-2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -38,6 +38,7 @@ type MailConfig struct {
SMTPHost string `yaml:"smtp_host"`
SMTPPort int `yaml:"smtp_port"`
SMTPTimeOut time.Duration `yaml:"smtp_timeout,omitempty"`
Domain string `yaml:"message_id_domain"`
NotificationSenderAddress string `yaml:"notification_sender_address"`
NoticeMailAddress string `yaml:"notice_mail_address"`
VoteNoticeMailAddress string `yaml:"vote_notice_mail_address"`
@ -202,10 +203,10 @@ func voteNoticeRecipient(mc *MailConfig) recipientData {
}
}
func motionReplyHeaders(m *models.Motion) map[string][]string {
func motionReplyHeaders(m *models.Motion, mc *MailConfig) map[string][]string {
return map[string][]string{
"References": {fmt.Sprintf("<%s>", m.Tag)},
"In-Reply-To": {fmt.Sprintf("<%s>", m.Tag)},
"References": {fmt.Sprintf("<%s@%s>", m.Tag, mc.Domain)},
"In-Reply-To": {fmt.Sprintf("<%s@%s>", m.Tag, mc.Domain)},
}
}
@ -247,7 +248,7 @@ func (c *ClosedDecisionNotification) GetNotificationContent(mc *MailConfig) *Not
*models.Motion
}{Motion: c.Decision},
subject: fmt.Sprintf("Re: %s - %s - finalized", c.Decision.Tag, c.Decision.Title),
headers: motionReplyHeaders(c.Decision),
headers: motionReplyHeaders(c.Decision, mc),
recipients: []recipientData{defaultRecipient(mc)},
}
}
@ -275,14 +276,14 @@ func (n NewDecisionNotification) GetNotificationContent(mc *MailConfig) *Notific
UnvotedURL: unvotedURL,
},
subject: fmt.Sprintf("%s - %s", n.Decision.Tag, n.Decision.Title),
headers: n.getHeaders(),
headers: n.getHeaders(mc),
recipients: []recipientData{defaultRecipient(mc)},
}
}
func (n NewDecisionNotification) getHeaders() map[string][]string {
func (n NewDecisionNotification) getHeaders(mc *MailConfig) map[string][]string {
return map[string][]string{
"Message-ID": {fmt.Sprintf("<%s>", n.Decision.Tag)},
"Message-ID": {fmt.Sprintf("<%s@%s>", n.Decision.Tag, mc.Domain)},
}
}
@ -309,7 +310,7 @@ func (u UpdateDecisionNotification) GetNotificationContent(mc *MailConfig) *Noti
UnvotedURL: unvotedURL,
},
subject: fmt.Sprintf("%s - %s", u.Decision.Tag, u.Decision.Title),
headers: motionReplyHeaders(u.Decision),
headers: motionReplyHeaders(u.Decision, mc),
recipients: []recipientData{defaultRecipient(mc)},
}
}
@ -333,7 +334,7 @@ func (d DirectVoteNotification) GetNotificationContent(mc *MailConfig) *Notifica
Choice: d.Choice,
},
subject: fmt.Sprintf("Re: %s - %s", d.Decision.Tag, d.Decision.Title),
headers: motionReplyHeaders(d.Decision),
headers: motionReplyHeaders(d.Decision, mc),
recipients: []recipientData{voteNoticeRecipient(mc)},
}
}
@ -363,7 +364,7 @@ func (p ProxyVoteNotification) GetNotificationContent(mc *MailConfig) *Notificat
Justification: p.Justification,
},
subject: fmt.Sprintf("Re: %s - %s", p.Decision.Tag, p.Decision.Title),
headers: motionReplyHeaders(p.Decision),
headers: motionReplyHeaders(p.Decision, mc),
recipients: []recipientData{voteNoticeRecipient(mc)},
}
}
@ -381,7 +382,7 @@ func (w WithDrawMotionNotification) GetNotificationContent(mc *MailConfig) *Noti
Name string
}{Motion: w.Motion, Name: w.Voter.Name},
subject: fmt.Sprintf("Re: %s - %s", w.Motion.Tag, w.Motion.Title),
headers: motionReplyHeaders(w.Motion),
headers: motionReplyHeaders(w.Motion, mc),
recipients: []recipientData{defaultRecipient(mc)},
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -19,7 +19,7 @@
{{ with .User }}
<div class="ui label">
<i class="id card outline icon"></i>
Authenticated as {{ .Name }} &lt;{{ .Reminder }}&gt;
Authenticated as {{ .Name }} &lt;{{ .Reminder.String }}&gt;
</div>
{{ end }}
</div>
@ -42,7 +42,7 @@
</main>
<footer class="ui vertical footer segment">
<div class="ui container">
<span class="ui small text">© 2017-2022 CAcert Inc.</span>
<span class="ui small text">© 2017-2023 CAcert Inc.</span>
</div>
</footer>
</body>