From fdd1a8abfd2ee6419f38a10f27ddddb9d9100fe9 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 10 Dec 2022 17:26:15 +0100 Subject: [PATCH 01/13] Crypto preperation (#165) * Decrypt prepare (#141) Prepare the vote service to support crypto votes in the future * Crypto preperation (#142) * Add crypto helper tool * Update vote-decrypt * Fix decoded vote field * Use new models.yml * Encode the stop value as (json-)string * Fix vote encoding * Reintoduce vote validate * Validate cryptographic vote on stop * Fix some todos * Fix go vet * Add route for the public main key * Fix postgres initialization * fix vote format * fix merge * update environment docu * fix tests * Add url test * Fix handler * Fix default postgres backend * Bump golang from 1.19.3-alpine to 1.19.4-alpine (#164) Bumps golang from 1.19.3-alpine to 1.19.4-alpine. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- README.md | 18 ++ environment.md | 6 +- go.mod | 15 +- go.sum | 31 ++- internal/backends/redis/redis.go | 3 - internal/vote/error.go | 8 - internal/vote/http.go | 77 +++++- internal/vote/http_run_test.go | 4 +- internal/vote/http_test.go | 32 ++- internal/vote/mock_test.go | 38 +++ internal/vote/vote.go | 374 ++++++++++++++++++---------- internal/vote/vote_test.go | 212 +++++++++++----- internal/vote/vote_validate_test.go | 11 +- main.go | 17 +- 15 files changed, 608 insertions(+), 240 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1399979..15ec866 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.19.3-alpine as base +FROM golang:1.19.4-alpine as base WORKDIR /root/ RUN apk add git diff --git a/README.md b/README.md index b20b534..9316ca9 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,24 @@ Response: ``` +### Crypto Public Main Key + +The vote service can return the public main service from vote-decrypt. The key +is decoded as base64. + +Example: + +``` +curl localhost:9013/internal/vote/public_main_key +``` + +Response: + +``` +dHR0dHR0dHR0ZWtmanRpd28zbmdrZGkxMjNuZmt3a3IK +``` + + ## Configuration The service is configurated with environment variables. See [all environment varialbes](environment.md). diff --git a/environment.md b/environment.md index 1eb7f20..d063b42 100644 --- a/environment.md +++ b/environment.md @@ -9,22 +9,24 @@ The Service uses the following environment variables: * `MESSAGE_BUS_HOST`: Host of the redis server. The default is `localhost`. * `MESSAGE_BUS_PORT`: Port of the redis server. The default is `6379`. * `DATASTORE_DATABASE_USER`: Postgres User. The default is `openslides`. -* `OPENSLIDES_DEVELOPMENT`: If set, the service uses the default secrets. The default is `false`. * `DATASTORE_DATABASE_HOST`: Postgres Host. The default is `localhost`. * `DATASTORE_DATABASE_PORT`: Postgres Post. The default is `5432`. * `DATASTORE_DATABASE_NAME`: Postgres Database. The default is `openslides`. +* `OPENSLIDES_DEVELOPMENT`: If set, the service uses the default secrets. The default is `false`. +* `SECRETS_PATH`: Path where the secrets are stored. The default is `/run/secrets`. * `AUTH_PROTOCOL`: Protocol of the auth service. The default is `http`. * `AUTH_HOST`: Host of the auth service. The default is `localhost`. * `AUTH_PORT`: Port of the auth service. The default is `9004`. * `AUTH_Fake`: Use user id 1 for every request. Ignores all other auth environment variables. The default is `false`. * `VOTE_REDIS_HOST`: Host of the redis used for the fast backend. The default is `localhost`. -* `VOTE_REDIS_PORT`: Port of the redis used for the fast backend. The default is `6370`. +* `VOTE_REDIS_PORT`: Port of the redis used for the fast backend. The default is `6379`. * `VOTE_DATABASE_USER`: Databasename of the postgres database used for long polls. The default is `postgres`. * `VOTE_DATABASE_HOST`: Host of the postgres database used for long polls. The default is `localhost`. * `VOTE_DATABASE_PORT`: Port of the postgres database used for long polls. The default is `5432`. * `VOTE_DATABASE_NAME`: The default is ``. * `VOTE_BACKEND_FAST`: The backend used for fast polls. Possible backends are redis, postgres or memory. The default is `redis`. * `VOTE_BACKEND_LONG`: The backend used for long polls. The default is `postgres`. +* `VOTE_DECRYPT_SERVICE`: Host and port of the decrypt service. The default is `localhost:9014`. ## Secrets diff --git a/go.mod b/go.mod index 78c1230..01978c8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,8 @@ module github.com/OpenSlides/openslides-vote-service go 1.19 require ( - github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20221114161157-a9fdb66c37a8 + github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20221119101955-2017de822931 + github.com/OpenSlides/vote-decrypt v0.0.0-20221012082621-2bbd25022db1 github.com/alecthomas/kong v0.7.1 github.com/gomodule/redigo v1.8.9 github.com/jackc/pgx/v5 v5.2.0 @@ -14,7 +15,7 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/docker/cli v20.10.21+incompatible // indirect github.com/docker/docker v20.10.21+incompatible // indirect @@ -22,6 +23,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/imdario/mergo v0.3.13 // indirect @@ -41,13 +43,16 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.2.0 // indirect - golang.org/x/mod v0.7.0 // indirect + golang.org/x/crypto v0.3.0 // indirect + golang.org/x/mod v0.6.0 // indirect golang.org/x/net v0.2.0 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect - golang.org/x/tools v0.3.0 // indirect + golang.org/x/tools v0.2.0 // indirect + google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e // indirect + google.golang.org/grpc v1.50.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 16d610b..542071e 100644 --- a/go.sum +++ b/go.sum @@ -5,14 +5,16 @@ github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2y github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20221114161157-a9fdb66c37a8 h1:vy0YLJHCHAFKnV6/tJvvJaV8KIvxBtpBfo0g98Ld6Kg= -github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20221114161157-a9fdb66c37a8/go.mod h1:X4HU2ZtD8llN1Nr9nJNzAXIQbnAIhy0muQgi1TK9GRg= +github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20221119101955-2017de822931 h1:UUBxCaoWE4h+1hidaVaYGX4VJBIMsXYLoi/1ZF6Xzpc= +github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20221119101955-2017de822931/go.mod h1:Vv13WxiEUPMhRFdVDR9NzhI6kiJ4Iqlzd0K6xLwPqoA= +github.com/OpenSlides/vote-decrypt v0.0.0-20221012082621-2bbd25022db1 h1:Cx+vhcfBkqO/BcZP2l4mVc1/Ga74mgMUAt6ueUOqnfo= +github.com/OpenSlides/vote-decrypt v0.0.0-20221012082621-2bbd25022db1/go.mod h1:rrD19LpkjiQK72mNbhKbyaOP4inuFuilYiZUVJ5ZgvA= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4= github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= @@ -44,6 +46,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -128,12 +132,12 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= -golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -170,14 +174,21 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e h1:halCgTFuLWDRD61piiNSxPsARANGD3Xl16hPrLgLiIg= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/grpc v1.50.0 h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/backends/redis/redis.go b/internal/backends/redis/redis.go index ce0d72a..a872ffa 100644 --- a/internal/backends/redis/redis.go +++ b/internal/backends/redis/redis.go @@ -185,9 +185,6 @@ func (b *Backend) Stop(ctx context.Context, pollID int) ([][]byte, []int, error) voteObjects = append(voteObjects, []byte(vote)) } - if log.IsDebug() { - log.Debug("Redis: Recieved %v", data) - } sort.Ints(userIDs) return voteObjects, userIDs, nil } diff --git a/internal/vote/error.go b/internal/vote/error.go index 160acab..f213e78 100644 --- a/internal/vote/error.go +++ b/internal/vote/error.go @@ -113,11 +113,3 @@ func (err MessageError) Error() string { func (err MessageError) Unwrap() error { return err.TypeError } - -// InvalidVote returns an error for invaid votes. -func InvalidVote(format string, a ...interface{}) MessageError { - return MessageError{ - ErrInvalid, - fmt.Sprintf(format, a...), - } -} diff --git a/internal/vote/http.go b/internal/vote/http.go index 2b290af..aecb616 100644 --- a/internal/vote/http.go +++ b/internal/vote/http.go @@ -36,6 +36,7 @@ func Run(ctx context.Context, lst net.Listener, auth authenticater, service *Vot handleVote(mux, service, auth) handleVoted(mux, service, auth) handleVoteCount(mux, service, ticketProvider) + handlePublicMainKey(mux, service) handleHealth(mux) srv := &http.Server{ @@ -63,7 +64,7 @@ func Run(ctx context.Context, lst net.Listener, auth authenticater, service *Vot } type starter interface { - Start(ctx context.Context, pollID int) error + Start(ctx context.Context, pollID int) ([]byte, []byte, error) } func handleStart(mux *http.ServeMux, start starter) { @@ -84,10 +85,23 @@ func handleStart(mux *http.ServeMux, start starter) { return } - if err := start.Start(r.Context(), id); err != nil { + pubkey, pubKeySig, err := start.Start(r.Context(), id) + if err != nil { handleError(w, err, true) return } + + content := struct { + PubKey []byte `json:"public_key"` + PubKeySig []byte `json:"public_key_sig"` + }{ + pubkey, + pubKeySig, + } + if err := json.NewEncoder(w).Encode(content); err != nil { + http.Error(w, MessageError{ErrInternal, err.Error()}.Error(), 500) + return + } }, ) } @@ -95,7 +109,7 @@ func handleStart(mux *http.ServeMux, start starter) { // stopper stops a poll. It sets the state of the poll, so that no other user // can vote. It writes the vote results to the writer. type stopper interface { - Stop(ctx context.Context, pollID int, w io.Writer) error + Stop(ctx context.Context, pollID int) (StopResult, error) } func handleStop(mux *http.ServeMux, stop stopper) { @@ -116,10 +130,40 @@ func handleStop(mux *http.ServeMux, stop stopper) { return } - if err := stop.Stop(r.Context(), id, w); err != nil { + stopResult, err := stop.Stop(r.Context(), id) + if err != nil { handleError(w, err, true) return } + + // Encode the votes object separatly to make it possible for the + // backend (python) to read its original value. + encodedVotes, err := json.Marshal(stopResult.Votes) + if err != nil { + handleError(w, fmt.Errorf("encoding votes: %w", err), true) + return + } + + if stopResult.UserIDs == nil { + stopResult.UserIDs = []int{} + } + + out := struct { + Votes string `json:"votes"` + Signature []byte `json:"signature"` + Users []int `json:"user_ids"` + Invalid map[int]string `json:"invalid,omitempty"` + }{ + string(encodedVotes), + stopResult.Signature, + stopResult.UserIDs, + stopResult.Invalid, + } + + if err := json.NewEncoder(w).Encode(out); err != nil { + handleError(w, fmt.Errorf("encoding and sending objects: %w", err), true) + return + } }, ) } @@ -344,6 +388,31 @@ func handleVoteCount(mux *http.ServeMux, voteCounter voteCounter, eventer func() ) } +type publicKeyer interface { + CryptoPublicMainKey(ctx context.Context) ([]byte, error) +} + +func handlePublicMainKey(mux *http.ServeMux, keyer publicKeyer) { + mux.HandleFunc( + httpPathInternal+"/public_main_key", + func(w http.ResponseWriter, r *http.Request) { + log.Info("Receiving public main key request") + w.Header().Set("Content-Type", "application/json") + + key, err := keyer.CryptoPublicMainKey(r.Context()) + if err != nil { + handleError(w, err, true) + return + } + + if err := json.NewEncoder(w).Encode(key); err != nil { + handleError(w, err, true) + return + } + }, + ) +} + func handleHealth(mux *http.ServeMux) { mux.HandleFunc( httpPathExternal+"/health", diff --git a/internal/vote/http_run_test.go b/internal/vote/http_run_test.go index 6bdb7b4..8c25b54 100644 --- a/internal/vote/http_run_test.go +++ b/internal/vote/http_run_test.go @@ -31,7 +31,8 @@ func TestRun(t *testing.T) { backend := memory.New() ds := dsmock.Stub{} - service := vote.New(backend, backend, ds) + decrypt := new(decrypterStub) + service := vote.New(backend, backend, ds, decrypt) getAddr := make(chan string) go func() { @@ -63,6 +64,7 @@ func TestRun(t *testing.T) { "/system/vote/voted", "/internal/vote/vote_count", "/system/vote/health", + "/internal/vote/public_main_key", } { resp, err := http.Get(fmt.Sprintf("http://%s%s", addr, url)) if err != nil { diff --git a/internal/vote/http_test.go b/internal/vote/http_test.go index 7567201..a6c8771 100644 --- a/internal/vote/http_test.go +++ b/internal/vote/http_test.go @@ -18,9 +18,9 @@ type starterStub struct { expectErr error } -func (c *starterStub) Start(ctx context.Context, pollID int) error { +func (c *starterStub) Start(ctx context.Context, pollID int) ([]byte, []byte, error) { c.id = pollID - return c.expectErr + return nil, nil, c.expectErr } func TestHandleStart(t *testing.T) { @@ -123,19 +123,26 @@ func TestHandleStart(t *testing.T) { } type stopperStub struct { - id int - expectWriter string - expectErr error + id int + expectErr error + + expectVotes json.RawMessage + expectedSignature []byte + expectedUserIDs []int } -func (s *stopperStub) Stop(ctx context.Context, pollID int, w io.Writer) error { +func (s *stopperStub) Stop(ctx context.Context, pollID int) (StopResult, error) { s.id = pollID if s.expectErr != nil { - return s.expectErr + return StopResult{}, s.expectErr } - _, err := w.Write([]byte(s.expectWriter)) - return err + + return StopResult{ + Votes: s.expectVotes, + Signature: s.expectedSignature, + UserIDs: s.expectedUserIDs, + }, nil } func TestHandleStop(t *testing.T) { @@ -164,7 +171,7 @@ func TestHandleStop(t *testing.T) { }) t.Run("Valid", func(t *testing.T) { - stopper.expectWriter = "some text" + stopper.expectVotes = []byte(`"some values"`) resp := httptest.NewRecorder() mux.ServeHTTP(resp, httptest.NewRequest("POST", url+"?id=1", nil)) @@ -177,8 +184,9 @@ func TestHandleStop(t *testing.T) { t.Errorf("Stopper was called with id %d, expected 1", stopper.id) } - if resp.Body.String() != stopper.expectWriter { - t.Errorf("Got body `%s`, expected `%s`", resp.Body.String(), stopper.expectWriter) + expect := `{"votes":"\"some values\"","signature":null,"user_ids":[]}` + if trimed := strings.TrimSpace(resp.Body.String()); trimed != expect { + t.Errorf("Got body `%s`, expected `%s`", trimed, expect) } }) diff --git a/internal/vote/mock_test.go b/internal/vote/mock_test.go index 6e836db..b9e1262 100644 --- a/internal/vote/mock_test.go +++ b/internal/vote/mock_test.go @@ -2,6 +2,8 @@ package vote_test import ( "context" + "encoding/json" + "fmt" "net/http" "testing" @@ -39,6 +41,42 @@ func (g *StubGetter) assertKeys(t *testing.T, keys ...dskey.Key) { } } +type decrypterStub struct{} + +func (d *decrypterStub) Start(ctx context.Context, pollID string) (pubKey []byte, pubKeySig []byte, err error) { + return nil, nil, nil +} + +func (d *decrypterStub) Stop(ctx context.Context, pollID string, voteList [][]byte) (decryptedContent, signature []byte, err error) { + votes := make([]json.RawMessage, len(voteList)) + for i, vote := range voteList { + votes[i] = vote + } + + content := struct { + ID string `json:"id"` + Votes []json.RawMessage `json:"votes"` + }{ + pollID, + votes, + } + + decryptedContent, err = json.Marshal(content) + if err != nil { + return nil, nil, fmt.Errorf("marshal decrypted content: %w", err) + } + + return decryptedContent, []byte("signature"), nil +} + +func (d *decrypterStub) Clear(ctx context.Context, pollID string) error { + return nil +} + +func (d *decrypterStub) PublicMainKey(ctx context.Context) ([]byte, error) { + return []byte("pub_main_key"), nil +} + type autherStub struct { userID int } diff --git a/internal/vote/vote.go b/internal/vote/vote.go index da4d797..c08ee78 100644 --- a/internal/vote/vote.go +++ b/internal/vote/vote.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "net/url" "github.com/OpenSlides/openslides-autoupdate-service/pkg/datastore" "github.com/OpenSlides/openslides-autoupdate-service/pkg/datastore/dsfetch" @@ -13,6 +14,14 @@ import ( "github.com/OpenSlides/openslides-vote-service/internal/log" ) +// Decrypter decryptes the incomming votes. +type Decrypter interface { + Start(ctx context.Context, pollID string) (pubKey []byte, pubKeySig []byte, err error) + Stop(ctx context.Context, pollID string, voteList [][]byte) (decryptedContent, signature []byte, err error) + Clear(ctx context.Context, pollID string) error + PublicMainKey(ctx context.Context) ([]byte, error) +} + // Vote holds the state of the service. // // Vote has to be initializes with vote.New(). @@ -20,14 +29,16 @@ type Vote struct { fastBackend Backend longBackend Backend ds datastore.Getter + decrypter Decrypter } // New creates an initializes vote service. -func New(fast, long Backend, ds datastore.Getter) *Vote { +func New(fast, long Backend, ds datastore.Getter, decrypter Decrypter) *Vote { return &Vote{ fastBackend: fast, longBackend: long, ds: ds, + decrypter: decrypter, } } @@ -41,12 +52,26 @@ func (v *Vote) backend(p pollConfig) Backend { return backend } +func (v *Vote) qualifiedID(ctx context.Context, fetch *dsfetch.Fetch, id int) (string, error) { + rawURL, err := fetch.Organization_Url(1).Value(ctx) + if err != nil { + return "", fmt.Errorf("getting organization url: %v", err) + } + + parsed, err := url.Parse(rawURL) + if err != nil { + return "", fmt.Errorf("invalid url %s: %w", rawURL, err) + } + + return fmt.Sprintf("%s/%d", parsed.Hostname(), id), nil +} + // Start an electronic vote. // // This function is idempotence. If you call it with the same input, you will // get the same output. This means, that when a poll is stopped, Start() will // not throw an error. -func (v *Vote) Start(ctx context.Context, pollID int) (err error) { +func (v *Vote) Start(ctx context.Context, pollID int) (pubkey []byte, pubKeySig []byte, err error) { log.Debug("Receive start event for poll %d", pollID) defer func() { log.Debug("End start event with error: %v", err) @@ -57,76 +82,142 @@ func (v *Vote) Start(ctx context.Context, pollID int) (err error) { poll, err := loadPoll(ctx, ds, pollID) if err != nil { - return fmt.Errorf("loading poll: %w", err) + return nil, nil, fmt.Errorf("loading poll: %w", err) } - if poll.pollType == "analog" { - return MessageError{ErrInvalid, "Analog poll can not be started"} + if poll.ptype == "analog" { + return nil, nil, MessageError{ErrInvalid, "Analog poll can not be started"} } if err := poll.preload(ctx, ds); err != nil { - return fmt.Errorf("preloading data: %w", err) + return nil, nil, fmt.Errorf("preloading data: %w", err) } log.Debug("Preload cache. Received keys: %v", recorder.Keys()) backend := v.backend(poll) if err := backend.Start(ctx, pollID); err != nil { - return fmt.Errorf("starting poll in the backend: %w", err) + return nil, nil, fmt.Errorf("starting poll in the backend: %w", err) } - return nil + if poll.ptype != "cryptographic" { + return nil, nil, nil + } + + defer func() { + if err != nil { + backend.Clear(ctx, pollID) + } + }() + + qid, err := v.qualifiedID(ctx, ds, pollID) + if err != nil { + return nil, nil, fmt.Errorf("building qualified id: %w", err) + } + + pubkey, pubKeySig, err = v.decrypter.Start(ctx, qid) + if err != nil { + return nil, nil, fmt.Errorf("starting poll in decrypter: %w", err) + } + + return pubkey, pubKeySig, nil +} + +// StopResult is the return value from vote.Stop. +type StopResult struct { + Votes json.RawMessage + Signature []byte + UserIDs []int + Invalid map[int]string } // Stop ends a poll. // // This method is idempotence. Many requests with the same pollID will return // the same data. Calling vote.Clear will stop this behavior. -func (v *Vote) Stop(ctx context.Context, pollID int, w io.Writer) (err error) { +func (v *Vote) Stop(ctx context.Context, pollID int) (StopResult, error) { log.Debug("Receive stop event for poll %d", pollID) - defer func() { - log.Debug("End stop event with error: %v", err) - }() ds := dsfetch.New(v.ds) poll, err := loadPoll(ctx, ds, pollID) if err != nil { - return fmt.Errorf("loading poll: %w", err) + return StopResult{}, fmt.Errorf("loading poll: %w", err) } backend := v.backend(poll) - objects, userIDs, err := backend.Stop(ctx, pollID) + ballots, userIDs, err := backend.Stop(ctx, pollID) if err != nil { var errNotExist interface{ DoesNotExist() } if errors.As(err, &errNotExist) { - return MessageError{ErrNotExists, fmt.Sprintf("Poll %d does not exist in the backend", pollID)} + return StopResult{}, MessageError{ErrNotExists, fmt.Sprintf("Poll %d does not exist in the backend", pollID)} } - return fmt.Errorf("fetching vote objects: %w", err) + return StopResult{}, fmt.Errorf("fetching vote objects: %w", err) } - // Convert vote objects to json.RawMessage - encodableObjects := make([]json.RawMessage, len(objects)) - for i := range objects { - encodableObjects[i] = objects[i] + switch poll.ptype { + case "cryptographic": + return v.stopCrypto(ctx, poll, ds, ballots, userIDs) + default: + return stopNonCrypto(ballots, userIDs) } +} - if userIDs == nil { - userIDs = []int{} +func stopNonCrypto(ballots [][]byte, userIDs []int) (StopResult, error) { + encodable := make([]json.RawMessage, len(ballots)) + for i := range ballots { + encodable[i] = ballots[i] } - out := struct { - Votes []json.RawMessage `json:"votes"` - Users []int `json:"user_ids"` - }{ - encodableObjects, - userIDs, + votes, err := json.Marshal(encodable) + if err != nil { + return StopResult{}, fmt.Errorf("encode votes to list: %w", err) + } + + return StopResult{Votes: votes, UserIDs: userIDs}, nil +} + +func (v *Vote) stopCrypto(ctx context.Context, poll pollConfig, ds *dsfetch.Fetch, ballots [][]byte, userIDs []int) (StopResult, error) { + qid, err := v.qualifiedID(ctx, ds, poll.id) + if err != nil { + return StopResult{}, fmt.Errorf("building qualified id: %w", err) } - if err := json.NewEncoder(w).Encode(out); err != nil { - return fmt.Errorf("encoding and sending objects: %w", err) + voteValue := make([][]byte, len(ballots)) + for i := range ballots { + // This uses the type `[]byte` to decode a base64 value. + var vote struct { + Value []byte `json:"value"` + } + if err := json.Unmarshal(ballots[i], &vote); err != nil { + return StopResult{}, fmt.Errorf("decoding stored vote: %w", err) + } + + voteValue[i] = vote.Value } - return nil + decrypted, signature, err := v.decrypter.Stop(ctx, qid, voteValue) + if err != nil { + return StopResult{}, fmt.Errorf("decrypting votes: %w", err) + } + + var decryptedContent struct { + ID string `json:"id"` + Votes []struct { + Votes ballotValue `json:"votes"` + } `json:"votes"` + } + if err := json.Unmarshal(decrypted, &decryptedContent); err != nil { + return StopResult{}, fmt.Errorf("encoding decrypted votes: %w", err) + } + + invalid := make(map[int]string) + for i, vote := range decryptedContent.Votes { + if validation := validate(poll, vote.Votes); validation != "" { + invalid[i] = validation + } + } + + return StopResult{Votes: decrypted, Signature: signature, UserIDs: userIDs, Invalid: invalid}, nil } // Clear removes all knowlage of a poll. @@ -144,10 +235,26 @@ func (v *Vote) Clear(ctx context.Context, pollID int) (err error) { return fmt.Errorf("clearing longBackend: %w", err) } + ds := dsfetch.New(v.ds) + qid, err := v.qualifiedID(ctx, ds, pollID) + if err != nil { + return fmt.Errorf("building qualified id: %w", err) + } + + if v.decrypter == nil { + return nil + } + + if err := v.decrypter.Clear(ctx, qid); err != nil { + return fmt.Errorf("clearing decrypter: %w", err) + } + return nil } // ClearAll removes all knowlage of all polls and the datastore-cache. +// +// This does not work for the vote decrypter. func (v *Vote) ClearAll(ctx context.Context) (err error) { log.Debug("Receive clearAll event") defer func() { @@ -174,11 +281,8 @@ func (v *Vote) ClearAll(ctx context.Context) (err error) { } // Vote validates and saves the vote. -func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) (err error) { +func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) error { log.Debug("Receive vote event for poll %d from user %d", pollID, requestUser) - defer func() { - log.Debug("End vote event with error: %v", err) - }() ds := dsfetch.New(v.ds) poll, err := loadPoll(ctx, ds, pollID) @@ -187,13 +291,8 @@ func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) ( } log.Debug("Poll config: %v", poll) - presentMeetings, err := ds.User_IsPresentInMeetingIDs(requestUser).Value(ctx) - if err != nil { - return fmt.Errorf("fetching is present in meetings: %w", err) - } - - if !isPresent(poll.meetingID, presentMeetings) { - return MessageError{ErrNotAllowed, fmt.Sprintf("You have to be present in meeting %d", poll.meetingID)} + if err := ensurePresent(ctx, ds, poll.meetingID, requestUser); err != nil { + return err } var vote ballot @@ -206,74 +305,39 @@ func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) ( voteUser = requestUser } - if voteUser == 0 { - return MessageError{ErrNotAllowed, "Votes for anonymous user are not allowed"} - } - - if err := vote.validate(poll); err != nil { - return fmt.Errorf("validating vote: %w", err) + if err := ensureVoteUser(ctx, ds, poll, voteUser, requestUser); err != nil { + return err } - backend := v.backend(poll) - - if voteUser != requestUser { - delegationActivated, err := ds.Meeting_UsersEnableVoteDelegations(poll.meetingID).Value(ctx) - if err != nil { - return fmt.Errorf("fetching user enable vote delegation: %w", err) - } - - if !delegationActivated { - return MessageError{ErrNotAllowed, fmt.Sprintf("Vote delegation is not activated in meeting %d", poll.meetingID)} + var voteWeight string + if poll.ptype != "cryptographic" { + if validation := validate(poll, vote.Value); validation != "" { + return MessageError{ErrInvalid, validation} } - delegation, err := ds.User_VoteDelegatedToID(voteUser, poll.meetingID).Value(ctx) - if err != nil { - // If the user from the request body does not exist, then delegation - // will be 0. This case is handled below. - var errDoesNotExist dsfetch.DoesNotExistError - if !errors.As(err, &errDoesNotExist) { - return fmt.Errorf("fetching delegation from user %d in meeting %d: %w", voteUser, poll.meetingID, err) + // voteData.Weight is a DecimalField with 6 zeros. + if ds.Meeting_UsersEnableVoteWeight(poll.meetingID).ErrorLater(ctx) { + voteWeight = ds.User_VoteWeight(voteUser, poll.meetingID).ErrorLater(ctx) + if voteWeight == "" { + voteWeight = ds.User_DefaultVoteWeight(voteUser).ErrorLater(ctx) } } - - if delegation != requestUser { - return MessageError{ErrNotAllowed, fmt.Sprintf("You can not vote for user %d", voteUser)} + if err := ds.Err(); err != nil { + return fmt.Errorf("getting vote weight: %w", err) } - log.Debug("Vote delegation") - } - - groupIDs, err := ds.User_GroupIDs(voteUser, poll.meetingID).Value(ctx) - if err != nil { - return fmt.Errorf("fetching groups of user %d in meeting %d: %w", voteUser, poll.meetingID, err) - } - - if !equalElement(groupIDs, poll.groups) { - return MessageError{ErrNotAllowed, fmt.Sprintf("User %d is not allowed to vote", voteUser)} - } - // voteData.Weight is a DecimalField with 6 zeros. - var voteWeight string - if ds.Meeting_UsersEnableVoteWeight(poll.meetingID).ErrorLater(ctx) { - voteWeight = ds.User_VoteWeight(voteUser, poll.meetingID).ErrorLater(ctx) if voteWeight == "" { - voteWeight = ds.User_DefaultVoteWeight(voteUser).ErrorLater(ctx) + voteWeight = "1.000000" } - } - if err := ds.Err(); err != nil { - return fmt.Errorf("getting vote weight: %w", err) - } - if voteWeight == "" { - voteWeight = "1.000000" + log.Debug("Using voteWeight %s", voteWeight) } - log.Debug("Using voteWeight %s", voteWeight) - voteData := struct { RequestUser int `json:"request_user_id,omitempty"` VoteUser int `json:"vote_user_id,omitempty"` Value json.RawMessage `json:"value"` - Weight string `json:"weight"` + Weight string `json:"weight,omitempty"` }{ requestUser, voteUser, @@ -281,7 +345,7 @@ func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) ( voteWeight, } - if poll.pollType == "pseudoanonymous" { + if poll.ptype != "named" { voteData.RequestUser = 0 voteData.VoteUser = 0 } @@ -291,7 +355,7 @@ func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) ( return fmt.Errorf("decoding vote data: %w", err) } - if err := backend.Vote(ctx, pollID, voteUser, bs); err != nil { + if err := v.backend(poll).Vote(ctx, pollID, voteUser, bs); err != nil { var errNotExist interface{ DoesNotExist() } if errors.As(err, &errNotExist) { return ErrNotExists @@ -313,6 +377,65 @@ func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) ( return nil } +// ensurePresent makes sure that the user sending the vote request is present. +func ensurePresent(ctx context.Context, ds *dsfetch.Fetch, meetingID, user int) error { + presentMeetings, err := ds.User_IsPresentInMeetingIDs(user).Value(ctx) + if err != nil { + return fmt.Errorf("fetching is present in meetings: %w", err) + } + + for _, present := range presentMeetings { + if present == meetingID { + return nil + } + } + return MessageError{ErrNotAllowed, fmt.Sprintf("You have to be present in meeting %d", meetingID)} +} + +// ensureVoteUser makes sure the user from the vote: +// * is not anonymous, +// * the delegation is correct and +// * is in the correct group +func ensureVoteUser(ctx context.Context, ds *dsfetch.Fetch, poll pollConfig, voteUser, requestUser int) error { + if voteUser == 0 { + return MessageError{ErrNotAllowed, "Votes for anonymous user are not allowed"} + } + + groupIDs, err := ds.User_GroupIDs(voteUser, poll.meetingID).Value(ctx) + if err != nil { + return fmt.Errorf("fetching groups of user %d in meeting %d: %w", voteUser, poll.meetingID, err) + } + + if !equalElement(groupIDs, poll.groups) { + return MessageError{ErrNotAllowed, fmt.Sprintf("User %d is not in a group that is allowed to vote", voteUser)} + } + + if voteUser == requestUser { + return nil + } + + delegationActivated, err := ds.Meeting_UsersEnableVoteDelegations(poll.meetingID).Value(ctx) + if err != nil { + return fmt.Errorf("fetching user enable vote delegation: %w", err) + } + + if !delegationActivated { + return MessageError{ErrNotAllowed, fmt.Sprintf("Vote delegation is not activated in meeting %d", poll.meetingID)} + } + + log.Debug("Vote delegation") + delegation, err := ds.User_VoteDelegatedToID(voteUser, poll.meetingID).Value(ctx) + if err != nil { + return fmt.Errorf("fetching delegation from user %d in meeting %d: %w", voteUser, poll.meetingID, err) + } + + if delegation != requestUser { + return MessageError{ErrNotAllowed, fmt.Sprintf("You can not vote for user %d", voteUser)} + } + + return nil +} + // VotedPolls tells, on which the requestUser has already voted. func (v *Vote) VotedPolls(ctx context.Context, pollIDs []int, requestUser int) (map[int][]int, error) { log.Debug("Receive voted event for polls %v from user %d", pollIDs, requestUser) @@ -424,6 +547,15 @@ func (v *Vote) VoteCount(ctx context.Context) (map[int]int, error) { return count, nil } +// CryptoPublicMainKey returns the public main key from vote-decrypt. +func (v *Vote) CryptoPublicMainKey(ctx context.Context) ([]byte, error) { + if v.decrypter == nil { + return nil, fmt.Errorf("decrypt service is not configured") + } + + return v.decrypter.PublicMainKey(ctx) +} + // Backend is a storage for the poll options. type Backend interface { // Start opens the poll for votes. To start a poll that is already started @@ -467,7 +599,7 @@ type pollConfig struct { id int meetingID int backend string - pollType string + ptype string method string groups []int globalYes bool @@ -484,7 +616,7 @@ func loadPoll(ctx context.Context, ds *dsfetch.Fetch, pollID int) (pollConfig, e p := pollConfig{id: pollID} ds.Poll_MeetingID(pollID).Lazy(&p.meetingID) ds.Poll_Backend(pollID).Lazy(&p.backend) - ds.Poll_Type(pollID).Lazy(&p.pollType) + ds.Poll_Type(pollID).Lazy(&p.ptype) ds.Poll_Pollmethod(pollID).Lazy(&p.method) ds.Poll_EntitledGroupIDs(pollID).Lazy(&p.groups) ds.Poll_GlobalYes(pollID).Lazy(&p.globalYes) @@ -590,7 +722,7 @@ func (v ballot) String() string { return string(bs) } -func (v *ballot) validate(poll pollConfig) error { +func validate(poll pollConfig, v ballotValue) string { if poll.minAmount == 0 { poll.minAmount = 1 } @@ -614,75 +746,74 @@ func (v *ballot) validate(poll pollConfig) error { "A": poll.globalAbstain, } - // Helper "error" that is not an error. Should help readability. - var voteIsValid error + var voteIsValid string switch poll.method { case "Y", "N": - switch v.Value.Type() { + switch v.Type() { case ballotValueString: // The user answered with Y, N or A (or another invalid string). - if !allowedGlobal[v.Value.str] { - return InvalidVote("Global vote %s is not enabled", v.Value.str) + if !allowedGlobal[v.str] { + return fmt.Sprintf("Global vote %s is not enabled", v.str) } return voteIsValid case ballotValueOptionAmount: var sumAmount int - for optionID, amount := range v.Value.optionAmount { + for optionID, amount := range v.optionAmount { if amount < 0 { - return InvalidVote("Your vote for option %d has to be >= 0", optionID) + return fmt.Sprintf("Your vote for option %d has to be >= 0", optionID) } if amount > poll.maxVotesPerOption { - return InvalidVote("Your vote for option %d has to be <= %d", optionID, poll.maxVotesPerOption) + return fmt.Sprintf("Your vote for option %d has to be <= %d", optionID, poll.maxVotesPerOption) } if !allowedOptions[optionID] { - return InvalidVote("Option_id %d does not belong to the poll", optionID) + return fmt.Sprintf("Option_id %d does not belong to the poll", optionID) } sumAmount += amount } if sumAmount < poll.minAmount || sumAmount > poll.maxAmount { - return InvalidVote("The sum of your answers has to be between %d and %d", poll.minAmount, poll.maxAmount) + return fmt.Sprintf("The sum of your answers has to be between %d and %d", poll.minAmount, poll.maxAmount) } return voteIsValid default: - return MessageError{ErrInvalid, "Your vote has a wrong format"} + return fmt.Sprintf("Your vote has a wrong format for poll method Y or N") } case "YN", "YNA": - switch v.Value.Type() { + switch v.Type() { case ballotValueString: // The user answered with Y, N or A (or another invalid string). - if !allowedGlobal[v.Value.str] { - return InvalidVote("Global vote %s is not enabled", v.Value.str) + if !allowedGlobal[v.str] { + return fmt.Sprintf("Global vote %s is not enabled", v.str) } return voteIsValid case ballotValueOptionString: - for optionID, yna := range v.Value.optionYNA { + for optionID, yna := range v.optionYNA { if !allowedOptions[optionID] { - return InvalidVote("Option_id %d does not belong to the poll", optionID) + return fmt.Sprintf("Option_id %d does not belong to the poll", optionID) } if yna != "Y" && yna != "N" && (yna != "A" || poll.method != "YNA") { // Valid that given data matches poll method. - return InvalidVote("Data for option %d does not fit the poll method.", optionID) + return fmt.Sprintf("Data for option %d does not fit the poll method.", optionID) } } return voteIsValid default: - return InvalidVote("Your vote has a wrong format") + return fmt.Sprintf("Your vote has a wrong format for poll method YN or YNA") } default: - return InvalidVote("Your vote has a wrong format") + return fmt.Sprintf("Invalid poll method") } } @@ -744,15 +875,6 @@ func (v *ballotValue) Type() int { return ballotValueUnknown } -func isPresent(meetingID int, presentMeetings []int) bool { - for _, present := range presentMeetings { - if present == meetingID { - return true - } - } - return false -} - // equalElement returns true, if g1 and g2 have at lease one equal element. func equalElement(g1, g2 []int) bool { set := make(map[int]bool, len(g1)) diff --git a/internal/vote/vote_test.go b/internal/vote/vote_test.go index 7de15e5..071dc2f 100644 --- a/internal/vote/vote_test.go +++ b/internal/vote/vote_test.go @@ -1,10 +1,11 @@ package vote_test import ( - "bytes" "context" + "encoding/base64" "encoding/json" "errors" + "fmt" "reflect" "strings" "testing" @@ -19,6 +20,7 @@ func TestVoteStart(t *testing.T) { t.Run("Not started poll", func(t *testing.T) { backend := memory.New() ds, _ := dsmock.NewMockDatastore(dsmock.YAMLData(` + organization/1/url: test.com poll: 1: meeting_id: 5 @@ -32,13 +34,13 @@ func TestVoteStart(t *testing.T) { meeting/5/id: 5 `)) - v := vote.New(backend, backend, ds) + v := vote.New(backend, backend, ds, new(decrypterStub)) - if err := v.Start(context.Background(), 1); err != nil { + if _, _, err := v.Start(context.Background(), 1); err != nil { t.Errorf("Start returned unexpected error: %v", err) } - if c := len(ds.Requests()); c > 2 { + if c := len(ds.Requests()); c > 3 { t.Errorf("Start used %d requests to the datastore, expected max 2: %v", c, ds.Requests()) } @@ -51,6 +53,7 @@ func TestVoteStart(t *testing.T) { t.Run("Start poll a second time", func(t *testing.T) { backend := memory.New() ds := StubGetter{data: dsmock.YAMLData(` + organization/1/url: test.com poll: 1: meeting_id: 5 @@ -63,10 +66,10 @@ func TestVoteStart(t *testing.T) { user/1/is_present_in_meeting_ids: [1] meeting/5/id: 5 `)} - v := vote.New(backend, backend, &ds) + v := vote.New(backend, backend, &ds, new(decrypterStub)) v.Start(context.Background(), 1) - if err := v.Start(context.Background(), 1); err != nil { + if _, _, err := v.Start(context.Background(), 1); err != nil { t.Errorf("Start returned unexpected error: %v", err) } }) @@ -74,6 +77,7 @@ func TestVoteStart(t *testing.T) { t.Run("Start a stopped poll", func(t *testing.T) { backend := memory.New() ds := StubGetter{data: dsmock.YAMLData(` + organization/1/url: test.com poll: 1: meeting_id: 5 @@ -86,14 +90,14 @@ func TestVoteStart(t *testing.T) { user/1/is_present_in_meeting_ids: [1] meeting/5/id: 5 `)} - v := vote.New(backend, backend, &ds) + v := vote.New(backend, backend, &ds, new(decrypterStub)) v.Start(context.Background(), 1) if _, _, err := backend.Stop(context.Background(), 1); err != nil { t.Fatalf("Stop returned unexpected error: %v", err) } - if err := v.Start(context.Background(), 1); err != nil { + if _, _, err := v.Start(context.Background(), 1); err != nil { t.Errorf("Start returned unexpected error: %v", err) } }) @@ -112,9 +116,9 @@ func TestVoteStart(t *testing.T) { group/1/user_ids: [1] user/1/is_present_in_meeting_ids: [1] `)} - v := vote.New(backend, backend, &ds) + v := vote.New(backend, backend, &ds, new(decrypterStub)) - err := v.Start(context.Background(), 1) + _, _, err := v.Start(context.Background(), 1) if err == nil { t.Errorf("Got no error, expected `Some error`") @@ -124,6 +128,7 @@ func TestVoteStart(t *testing.T) { t.Run("Start an poll in `wrong` state", func(t *testing.T) { backend := memory.New() ds := StubGetter{data: dsmock.YAMLData(` + organization/1/url: test.com poll: 1: meeting_id: 5 @@ -136,9 +141,9 @@ func TestVoteStart(t *testing.T) { user/1/is_present_in_meeting_ids: [1] meeting/5/id: 5 `)} - v := vote.New(backend, backend, &ds) + v := vote.New(backend, backend, &ds, new(decrypterStub)) - err := v.Start(context.Background(), 1) + _, _, err := v.Start(context.Background(), 1) if err != nil { t.Errorf("Start returned: %v", err) @@ -159,9 +164,9 @@ func TestVoteStart(t *testing.T) { group/1/user_ids: [1] user/1/is_present_in_meeting_ids: [1] `)} - v := vote.New(backend, backend, &ds) + v := vote.New(backend, backend, &ds, new(decrypterStub)) - err := v.Start(context.Background(), 1) + _, _, err := v.Start(context.Background(), 1) if err == nil { t.Errorf("Got no error, expected `Some error`") @@ -182,9 +187,9 @@ func TestVoteStart(t *testing.T) { group/1/user_ids: [1] user/1/is_present_in_meeting_ids: [1] `)} - v := vote.New(backend, backend, &ds) + v := vote.New(backend, backend, &ds, new(decrypterStub)) - err := v.Start(context.Background(), 1) + _, _, err := v.Start(context.Background(), 1) if err == nil { t.Errorf("Got no error, expected `Some error`") @@ -195,6 +200,7 @@ func TestVoteStart(t *testing.T) { func TestVoteStartPreloadData(t *testing.T) { backend := memory.New() ds, _ := dsmock.NewMockDatastore(dsmock.YAMLData(` + organization/1/url: test.com poll/1: meeting_id: 5 entitled_group_ids: [1] @@ -213,9 +219,9 @@ func TestVoteStartPreloadData(t *testing.T) { is_present_in_meeting_ids: [1] meeting/5/id: 5 `)) - v := vote.New(backend, backend, ds) + v := vote.New(backend, backend, ds, new(decrypterStub)) - if err := v.Start(context.Background(), 1); err != nil { + if _, _, err := v.Start(context.Background(), 1); err != nil { t.Errorf("Start returned unexpected error: %v", err) } @@ -227,8 +233,8 @@ func TestVoteStartPreloadData(t *testing.T) { func TestVoteStartDSError(t *testing.T) { backend := memory.New() ds := StubGetter{err: errors.New("Some error")} - v := vote.New(backend, backend, &ds) - err := v.Start(context.Background(), 1) + v := vote.New(backend, backend, &ds, new(decrypterStub)) + _, _, err := v.Start(context.Background(), 1) if err == nil { t.Errorf("Got no error, expected `Some error`") @@ -238,6 +244,7 @@ func TestVoteStartDSError(t *testing.T) { func TestVoteStop(t *testing.T) { backend := memory.New() v := vote.New(backend, backend, &StubGetter{data: dsmock.YAMLData(` + organization/1/url: test.com poll: 1: meeting_id: 1 @@ -254,11 +261,11 @@ func TestVoteStop(t *testing.T) { backend: fast type: pseudoanonymous pollmethod: Y - `)}) + `)}, new(decrypterStub)) t.Run("Unknown poll", func(t *testing.T) { - buf := new(bytes.Buffer) - err := v.Stop(context.Background(), 1, buf) + _, err := v.Stop(context.Background(), 1) + if !errors.Is(err, vote.ErrNotExists) { t.Errorf("Stopping an unknown poll has to return an ErrNotExists, got: %v", err) } @@ -269,20 +276,27 @@ func TestVoteStop(t *testing.T) { t.Fatalf("Start returned an unexpected error: %v", err) } - backend.Vote(context.Background(), 2, 1, []byte(`"polldata1"`)) - backend.Vote(context.Background(), 2, 2, []byte(`"polldata2"`)) + backend.Vote(context.Background(), 2, 1, []byte(`{"value":"polldata1"}`)) + backend.Vote(context.Background(), 2, 2, []byte(`{"value":"polldata2"}`)) - buf := new(bytes.Buffer) - if err := v.Stop(context.Background(), 2, buf); err != nil { + stopResult, err := v.Stop(context.Background(), 2) + if err != nil { t.Fatalf("Stop returned unexpected error: %v", err) } - expect := `{"votes":["polldata1","polldata2"],"user_ids":[1,2]}` - if got := strings.TrimSpace(buf.String()); got != expect { - t.Errorf("Stop wrote `%s`, expected `%s`", got, expect) + votes := stopResult.Votes + userIDs := stopResult.UserIDs + + expected := `[{"value":"polldata1"},{"value":"polldata2"}]` + if string(votes) != expected { + t.Errorf("Got votes %s, expected %s", votes, expected) + } + + if !reflect.DeepEqual(userIDs, []int{1, 2}) { + t.Errorf("Got users %v, expected [1 2]", userIDs) } - err := backend.Vote(context.Background(), 2, 3, []byte(`"polldata3"`)) + err = backend.Vote(context.Background(), 2, 3, []byte(`"polldata3"`)) var errStopped interface{ Stopped() } if !errors.As(err, &errStopped) { t.Errorf("Stop did not stop the poll in the backend.") @@ -294,21 +308,110 @@ func TestVoteStop(t *testing.T) { t.Fatalf("Start returned an unexpected error: %v", err) } - buf := new(bytes.Buffer) - if err := v.Stop(context.Background(), 3, buf); err != nil { + stopResult, err := v.Stop(context.Background(), 3) + if err != nil { t.Fatalf("Stop returned unexpected error: %v", err) } - expect := `{"votes":[],"user_ids":[]}` - if got := strings.TrimSpace(buf.String()); got != expect { - t.Errorf("Stop wrote `%s`, expected `%s`", got, expect) + votes := stopResult.Votes + userIDs := stopResult.UserIDs + + if string(votes) != `[]` { + t.Errorf("Got votes %s, expected []", votes) + } + + if len(userIDs) != 0 { + t.Errorf("Got userIDs %v, expected []", userIDs) } }) } +func TestVoteStopCrypto(t *testing.T) { + data := dsmock.YAMLData(` + organization/1/url: test.com + poll/1: + meeting_id: 1 + type: cryptographic + pollmethod: YN + global_yes: true + global_no: true + backend: fast + `) + backend := memory.New() + v := vote.New(backend, backend, dsmock.Stub(data), new(decrypterStub)) + + if err := backend.Start(context.Background(), 1); err != nil { + t.Fatalf("Start returned an unexpected error: %v", err) + } + + polldata1 := base64.StdEncoding.EncodeToString([]byte(`{"votes":"Y"}`)) + polldata2 := base64.StdEncoding.EncodeToString([]byte(`{"votes":"N"}`)) + + backend.Vote(context.Background(), 1, 1, []byte(fmt.Sprintf(`{"value":"%s"}`, polldata1))) + backend.Vote(context.Background(), 1, 2, []byte(fmt.Sprintf(`{"value":"%s"}`, polldata2))) + + stopResult, err := v.Stop(context.Background(), 1) + if err != nil { + t.Fatalf("Stop returned unexpected error: %v", err) + } + + expected := vote.StopResult{ + Votes: []byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"N"}]}`), + Signature: []byte("signature"), + UserIDs: []int{1, 2}, + Invalid: map[int]string{}, + } + + if !reflect.DeepEqual(stopResult, expected) { + t.Errorf("\nGot\t\t\t%v\nexpected\t%v (result.Votes: %s)", stopResult, expected, stopResult.Votes) + } +} + +func TestVoteStopCryptoInvalid(t *testing.T) { + data := dsmock.YAMLData(` + organization/1/url: test.com + poll/1: + meeting_id: 1 + type: cryptographic + pollmethod: YN + global_yes: true + global_no: true + backend: fast + `) + backend := memory.New() + v := vote.New(backend, backend, dsmock.Stub(data), new(decrypterStub)) + + if err := backend.Start(context.Background(), 1); err != nil { + t.Fatalf("Start returned an unexpected error: %v", err) + } + + polldata1 := base64.StdEncoding.EncodeToString([]byte(`{"votes":"Y"}`)) + polldata2 := base64.StdEncoding.EncodeToString([]byte(`{"votes":"Invalid"}`)) + + backend.Vote(context.Background(), 1, 1, []byte(fmt.Sprintf(`{"value":"%s"}`, polldata1))) + backend.Vote(context.Background(), 1, 2, []byte(fmt.Sprintf(`{"value":"%s"}`, polldata2))) + + stopResult, err := v.Stop(context.Background(), 1) + if err != nil { + t.Fatalf("Stop returned unexpected error: %v", err) + } + + expected := vote.StopResult{ + Votes: []byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"Invalid"}]}`), + Signature: []byte("signature"), + UserIDs: []int{1, 2}, + Invalid: map[int]string{1: "Global vote Invalid is not enabled"}, + } + + if !reflect.DeepEqual(stopResult, expected) { + t.Errorf("\nGot\t\t\t%v\nexpected\t%v (result.Votes: %s)", stopResult, expected, stopResult.Votes) + } +} + func TestVoteClear(t *testing.T) { + data := &StubGetter{data: dsmock.YAMLData(`organization/1/url: test.com`)} backend := memory.New() - v := vote.New(backend, backend, &StubGetter{}) + v := vote.New(backend, backend, data, new(decrypterStub)) if err := v.Clear(context.Background(), 1); err != nil { t.Fatalf("Clear returned unexpected error: %v", err) @@ -317,7 +420,7 @@ func TestVoteClear(t *testing.T) { func TestVoteClearAll(t *testing.T) { backend := memory.New() - v := vote.New(backend, backend, &StubGetter{}) + v := vote.New(backend, backend, &StubGetter{}, new(decrypterStub)) if err := v.ClearAll(context.Background()); err != nil { t.Fatalf("ClearAll returned unexpected error: %v", err) @@ -342,7 +445,7 @@ func TestVoteVote(t *testing.T) { is_present_in_meeting_ids: [1] group_$1_ids: [1] `), - }) + }, new(decrypterStub)) t.Run("Unknown poll", func(t *testing.T) { err := v.Vote(context.Background(), 1, 1, strings.NewReader(`{"value":"Y"}`)) @@ -369,19 +472,6 @@ func TestVoteVote(t *testing.T) { } }) - t.Run("Invalid format", func(t *testing.T) { - err := v.Vote(context.Background(), 1, 1, strings.NewReader(`{}`)) - - var errTyped vote.TypeError - if !errors.As(err, &errTyped) { - t.Fatalf("Vote() did not return an TypeError, got: %v", err) - } - - if errTyped != vote.ErrInvalid { - t.Errorf("Got error type `%s`, expected `%s`", errTyped.Type(), vote.ErrInvalid.Type()) - } - }) - t.Run("Valid data", func(t *testing.T) { err := v.Vote(context.Background(), 1, 1, strings.NewReader(`{"value":"Y"}`)) if err != nil { @@ -436,6 +526,7 @@ func TestVoteNoRequests(t *testing.T) { { "normal vote", `--- + organization/1/url: test.com poll/1: meeting_id: 50 entitled_group_ids: [5] @@ -458,6 +549,7 @@ func TestVoteNoRequests(t *testing.T) { { "delegation vote", `--- + organization/1/url: test.com poll/1: meeting_id: 50 entitled_group_ids: [5] @@ -483,6 +575,7 @@ func TestVoteNoRequests(t *testing.T) { { "vote weight enabled", `--- + organization/1/url: test.com poll/1: meeting_id: 50 entitled_group_ids: [5] @@ -507,6 +600,7 @@ func TestVoteNoRequests(t *testing.T) { { "vote weight enabled and delegated", `--- + organization/1/url: test.com poll/1: meeting_id: 50 entitled_group_ids: [5] @@ -535,9 +629,9 @@ func TestVoteNoRequests(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ds, _ := dsmock.NewMockDatastore(dsmock.YAMLData(tt.data)) backend := memory.New() - v := vote.New(backend, backend, ds) + v := vote.New(backend, backend, ds, new(decrypterStub)) - if err := v.Start(context.Background(), 1); err != nil { + if _, _, err := v.Start(context.Background(), 1); err != nil { t.Fatalf("Can not start poll: %v", err) } @@ -812,7 +906,7 @@ func TestVoteDelegationAndGroup(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { backend := memory.New() - v := vote.New(backend, backend, &StubGetter{data: dsmock.YAMLData(tt.data)}) + v := vote.New(backend, backend, &StubGetter{data: dsmock.YAMLData(tt.data)}, new(decrypterStub)) backend.Start(context.Background(), 1) err := v.Vote(context.Background(), 1, 1, strings.NewReader(tt.vote)) @@ -945,7 +1039,7 @@ func TestVoteWeight(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { backend := memory.New() - v := vote.New(backend, backend, &StubGetter{data: dsmock.YAMLData(tt.data)}) + v := vote.New(backend, backend, &StubGetter{data: dsmock.YAMLData(tt.data)}, new(decrypterStub)) backend.Start(context.Background(), 1) if err := v.Vote(context.Background(), 1, 1, strings.NewReader(`{"value":"Y"}`)); err != nil { @@ -983,7 +1077,7 @@ func TestVotedPolls(t *testing.T) { user/5/id: 5 `)) - v := vote.New(backend, backend, ds) + v := vote.New(backend, backend, ds, new(decrypterStub)) backend.Start(context.Background(), 1) backend.Vote(context.Background(), 1, 5, []byte(`"Y"`)) @@ -1011,7 +1105,7 @@ func TestVotedPollsWithDelegation(t *testing.T) { vote_delegations_$_from_ids: ["8"] vote_delegations_$8_from_ids: [11,12] `)) - v := vote.New(backend, backend, ds) + v := vote.New(backend, backend, ds, new(decrypterStub)) backend.Start(context.Background(), 1) backend.Vote(context.Background(), 1, 5, []byte(`"Y"`)) backend.Vote(context.Background(), 1, 10, []byte(`"Y"`)) @@ -1038,7 +1132,7 @@ func TestVoteCount(t *testing.T) { backend2.Vote(context.Background(), 42, 2, []byte("vote")) ds := dsmock.Stub(dsmock.YAMLData(``)) - v := vote.New(backend1, backend2, ds) + v := vote.New(backend1, backend2, ds, new(decrypterStub)) count, err := v.VoteCount(context.Background()) if err != nil { diff --git a/internal/vote/vote_validate_test.go b/internal/vote/vote_validate_test.go index 7a20c93..270653f 100644 --- a/internal/vote/vote_validate_test.go +++ b/internal/vote/vote_validate_test.go @@ -2,7 +2,6 @@ package vote import ( "encoding/json" - "errors" "testing" ) @@ -300,17 +299,17 @@ func TestVoteValidate(t *testing.T) { t.Fatalf("decoding vote: %v", err) } - err := b.validate(tt.poll) + validation := validate(tt.poll, b.Value) if tt.expectValid { - if err != nil { - t.Fatalf("Validate returned unexpected error: %v", err) + if validation != "" { + t.Fatalf("Validate returned unexpected message: %s", validation) } return } - if !errors.Is(err, ErrInvalid) { - t.Fatalf("Expected ErrInvalid, got: %v", err) + if validation == "" { + t.Errorf("Got no validation error") } }) } diff --git a/main.go b/main.go index fb56347..3cb2531 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "github.com/OpenSlides/openslides-vote-service/internal/backends/redis" "github.com/OpenSlides/openslides-vote-service/internal/log" "github.com/OpenSlides/openslides-vote-service/internal/vote" + "github.com/OpenSlides/vote-decrypt/grpc" "github.com/alecthomas/kong" ) @@ -33,9 +34,11 @@ var ( envPostgresHost = environment.NewVariable("VOTE_DATABASE_HOST", "localhost", "Host of the postgres database used for long polls.") envPostgresPort = environment.NewVariable("VOTE_DATABASE_PORT", "5432", "Port of the postgres database used for long polls.") - envPostgresUser = environment.NewVariable("VOTE_DATABASE_USER", "postgres", "Databasename of the postgres database used for long polls.") - envPostgresDatabase = environment.NewVariable("VOTE_DATABASE_NAME", "", "") + envPostgresUser = environment.NewVariable("VOTE_DATABASE_USER", "openslides", "Databasename of the postgres database used for long polls.") + envPostgresDatabase = environment.NewVariable("VOTE_DATABASE_NAME", "openslides", "") envPostgresPassword = environment.NewSecret("postgres_password", "Password of the postgres database used for long polls.") + + envVoteDecryptService = environment.NewVariable("VOTE_DECRYPT_SERVICE", "localhost:9014", "Host and port of the decrypt service.") ) var cli struct { @@ -126,6 +129,8 @@ func initService(lookup environment.Environmenter) (func(context.Context) error, fastBackendStarter, longBackendStarter := buildBackends(lookup) + decryptAddr := envVoteDecryptService.Value(lookup) + service := func(ctx context.Context) error { for _, bg := range backgroundTasks { go bg(ctx, handleError) @@ -141,7 +146,13 @@ func initService(lookup environment.Environmenter) (func(context.Context) error, return fmt.Errorf("start long backend: %w", err) } - voteService := vote.New(fastBackend, longBackend, datastoreService) + decrypter, close, err := grpc.NewClient(decryptAddr) + if err != nil { + return fmt.Errorf("connection to vote decrypt service via grpc: %w", err) + } + defer close() + + voteService := vote.New(fastBackend, longBackend, datastoreService, decrypter) lst, err := net.Listen("tcp", listenAddr) if err != nil { From 03b540f138f37ba9d859e5cb983b4c92756885c5 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 4 Mar 2023 09:40:02 +0100 Subject: [PATCH 02/13] Update vote-decrypt version --- go.mod | 7 +++---- go.sum | 13 ++++++------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 53c8b7d..c64d786 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c - github.com/OpenSlides/vote-decrypt v0.0.0-20221012082621-2bbd25022db1 + github.com/OpenSlides/vote-decrypt v0.0.0-20230304083156-533a12cfc9f0 github.com/alecthomas/kong v0.7.1 github.com/gomodule/redigo v1.8.9 github.com/jackc/pgx/v5 v5.3.1 @@ -24,7 +24,6 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -49,8 +48,8 @@ require ( golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect golang.org/x/tools v0.5.0 // indirect - google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e // indirect - google.golang.org/grpc v1.50.0 // indirect + google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 // indirect + google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 802bf6b..9289060 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c h1:/atD4G3jmfvsWfdtUePUs86Hr8qkOqC9Z8b4rTKj12A= github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c/go.mod h1:HFtyW133MGuoPtJLMUmSgdVihhzoDUJla9/S4TTyP74= -github.com/OpenSlides/vote-decrypt v0.0.0-20221012082621-2bbd25022db1 h1:Cx+vhcfBkqO/BcZP2l4mVc1/Ga74mgMUAt6ueUOqnfo= -github.com/OpenSlides/vote-decrypt v0.0.0-20221012082621-2bbd25022db1/go.mod h1:rrD19LpkjiQK72mNbhKbyaOP4inuFuilYiZUVJ5ZgvA= +github.com/OpenSlides/vote-decrypt v0.0.0-20230304083156-533a12cfc9f0 h1:fuSvGtCmfMcXVJjCykz9b7/vslJzETxyXRkN+B84dYU= +github.com/OpenSlides/vote-decrypt v0.0.0-20230304083156-533a12cfc9f0/go.mod h1:0kJ4rcip5R12bO6wFBVxmdLq2VLbsisK+aIrbzH1FpU= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4= github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= @@ -53,7 +53,6 @@ github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs0 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -177,10 +176,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e h1:halCgTFuLWDRD61piiNSxPsARANGD3Xl16hPrLgLiIg= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/grpc v1.50.0 h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 h1:QQF+HdiI4iocoxUjjpLgvTYDHKm99C/VtTBFnfiCJos= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= From 38491d84484d4f69371149a416c1c2ef4bd5b9ca Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 4 Mar 2023 09:44:14 +0100 Subject: [PATCH 03/13] Fix type "douple" -> "double" --- internal/backends/memory/memory.go | 6 +++--- internal/backends/postgres/postgres.go | 6 +++--- internal/backends/redis/redis.go | 6 +++--- internal/backends/test/test.go | 9 +++------ internal/vote/error.go | 2 +- internal/vote/http_test.go | 4 ++-- internal/vote/vote.go | 6 +++--- 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/internal/backends/memory/memory.go b/internal/backends/memory/memory.go index 4633b4a..57b2ae3 100644 --- a/internal/backends/memory/memory.go +++ b/internal/backends/memory/memory.go @@ -64,7 +64,7 @@ func (b *Backend) Vote(ctx context.Context, pollID int, userID int, object []byt } if _, ok := b.voted[pollID][userID]; ok { - return doupleVoteError{fmt.Errorf("user has already voted")} + return doubleVoteError{fmt.Errorf("user has already voted")} } b.voted[pollID][userID] = true @@ -158,11 +158,11 @@ type doesNotExistError struct { func (doesNotExistError) DoesNotExist() {} -type doupleVoteError struct { +type doubleVoteError struct { error } -func (doupleVoteError) DoupleVote() {} +func (doubleVoteError) DoubleVote() {} type stoppedError struct { error diff --git a/internal/backends/postgres/postgres.go b/internal/backends/postgres/postgres.go index 308aae0..08e11a3 100644 --- a/internal/backends/postgres/postgres.go +++ b/internal/backends/postgres/postgres.go @@ -428,7 +428,7 @@ func (u *userIDList) add(userID int32) error { ints := []int32(*u) idx := sort.Search(len(ints), func(i int) bool { return ints[i] >= userID }) if idx < len(ints) && ints[idx] == userID { - return doupleVoteError{fmt.Errorf("User has already voted")} + return doubleVoteError{fmt.Errorf("User has already voted")} } // Insert the index at the correct order. @@ -454,11 +454,11 @@ type doesNotExistError struct { func (doesNotExistError) DoesNotExist() {} -type doupleVoteError struct { +type doubleVoteError struct { error } -func (doupleVoteError) DoupleVote() {} +func (doubleVoteError) DoubleVote() {} type stoppedError struct { error diff --git a/internal/backends/redis/redis.go b/internal/backends/redis/redis.go index a872ffa..a84ddf6 100644 --- a/internal/backends/redis/redis.go +++ b/internal/backends/redis/redis.go @@ -143,7 +143,7 @@ func (b *Backend) Vote(ctx context.Context, pollID int, userID int, object []byt case 2: return stoppedError{fmt.Errorf("poll is stopped")} case 3: - return doupleVoteError{fmt.Errorf("user has voted")} + return doubleVoteError{fmt.Errorf("user has voted")} default: return nil } @@ -323,11 +323,11 @@ type doesNotExistError struct { func (doesNotExistError) DoesNotExist() {} -type doupleVoteError struct { +type doubleVoteError struct { error } -func (doupleVoteError) DoupleVote() {} +func (doubleVoteError) DoubleVote() {} type stoppedError struct { error diff --git a/internal/backends/test/test.go b/internal/backends/test/test.go index 1b919ad..3912ebf 100644 --- a/internal/backends/test/test.go +++ b/internal/backends/test/test.go @@ -131,9 +131,9 @@ func Backend(t *testing.T, backend vote.Backend) { t.Fatalf("Second vote did not return an error") } - var errDoupleVote interface{ DoupleVote() } - if !errors.As(err, &errDoupleVote) { - t.Fatalf("Vote has to return a error with method DoupleVote. Got: %v", err) + var errDoubleVote interface{ DoubleVote() } + if !errors.As(err, &errDoubleVote) { + t.Fatalf("Vote has to return a error with method DoubleVote. Got: %v", err) } }) @@ -392,7 +392,6 @@ func Backend(t *testing.T, backend vote.Backend) { defer wg.Done() err := backend.Vote(context.Background(), pollID, uid, []byte("vote")) - if err != nil { var errStopped interface{ Stopped() } if errors.As(err, &errStopped) { @@ -405,7 +404,6 @@ func Backend(t *testing.T, backend vote.Backend) { t.Errorf("Vote %d returned undexpected error: %v", uid, err) } - }(i + 1) } @@ -418,7 +416,6 @@ func Backend(t *testing.T, backend vote.Backend) { defer wg.Done() obj, userIDs, err := backend.Stop(context.Background(), pollID) - if err != nil { t.Errorf("Stop returned undexpected error: %v", err) return diff --git a/internal/vote/error.go b/internal/vote/error.go index f213e78..8834197 100644 --- a/internal/vote/error.go +++ b/internal/vote/error.go @@ -47,7 +47,7 @@ func (err TypeError) Type() string { return "invalid" case ErrDoubleVote: - return "douple-vote" + return "double-vote" case ErrNotAllowed: return "not-allowed" diff --git a/internal/vote/http_test.go b/internal/vote/http_test.go index a6c8771..c94f295 100644 --- a/internal/vote/http_test.go +++ b/internal/vote/http_test.go @@ -434,8 +434,8 @@ func TestHandleVote(t *testing.T) { t.Fatalf("decoding resp body: %v", err) } - if body.Error != "douple-vote" { - t.Errorf("Got error `%s`, expected `douple-vote`", body.Error) + if body.Error != "double-vote" { + t.Errorf("Got error `%s`, expected `double-vote`", body.Error) } }) diff --git a/internal/vote/vote.go b/internal/vote/vote.go index c08ee78..5f5e658 100644 --- a/internal/vote/vote.go +++ b/internal/vote/vote.go @@ -361,8 +361,8 @@ func (v *Vote) Vote(ctx context.Context, pollID, requestUser int, r io.Reader) e return ErrNotExists } - var errDoupleVote interface{ DoupleVote() } - if errors.As(err, &errDoupleVote) { + var errDoubleVote interface{ DoubleVote() } + if errors.As(err, &errDoubleVote) { return ErrDoubleVote } @@ -566,7 +566,7 @@ type Backend interface { // Vote saves vote data into the backend. The backend has to check that the // poll is started and the userID has not voted before. // - // If the user has already voted, an Error with method `DoupleVote()` has to + // If the user has already voted, an Error with method `DoubleVote()` has to // be returned. If the poll has not started, an error with the method // `DoesNotExist()` is required. An a stopped vote, it has to be `Stopped()`. // From c3ff3d631c8c54ad5261e738258f12c422bd15c5 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 15 Mar 2023 14:27:47 +0100 Subject: [PATCH 04/13] Update deps --- environment.md | 4 ++-- go.mod | 26 ++++++++++++------------- go.sum | 52 +++++++++++++++++++++++++------------------------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/environment.md b/environment.md index d063b42..76d2ac0 100644 --- a/environment.md +++ b/environment.md @@ -20,10 +20,10 @@ The Service uses the following environment variables: * `AUTH_Fake`: Use user id 1 for every request. Ignores all other auth environment variables. The default is `false`. * `VOTE_REDIS_HOST`: Host of the redis used for the fast backend. The default is `localhost`. * `VOTE_REDIS_PORT`: Port of the redis used for the fast backend. The default is `6379`. -* `VOTE_DATABASE_USER`: Databasename of the postgres database used for long polls. The default is `postgres`. +* `VOTE_DATABASE_USER`: Databasename of the postgres database used for long polls. The default is `openslides`. * `VOTE_DATABASE_HOST`: Host of the postgres database used for long polls. The default is `localhost`. * `VOTE_DATABASE_PORT`: Port of the postgres database used for long polls. The default is `5432`. -* `VOTE_DATABASE_NAME`: The default is ``. +* `VOTE_DATABASE_NAME`: The default is `openslides`. * `VOTE_BACKEND_FAST`: The backend used for fast polls. Possible backends are redis, postgres or memory. The default is `redis`. * `VOTE_BACKEND_LONG`: The backend used for long polls. The default is `postgres`. * `VOTE_DECRYPT_SERVICE`: Host and port of the decrypt service. The default is `localhost:9014`. diff --git a/go.mod b/go.mod index c64d786..ca7c9b7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c - github.com/OpenSlides/vote-decrypt v0.0.0-20230304083156-533a12cfc9f0 + github.com/OpenSlides/vote-decrypt v0.0.0-20230314183154-52d9ebb2fd19 github.com/alecthomas/kong v0.7.1 github.com/gomodule/redigo v1.8.9 github.com/jackc/pgx/v5 v5.3.1 @@ -17,13 +17,13 @@ require ( github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/containerd/continuity v0.3.0 // indirect - github.com/docker/cli v23.0.0+incompatible // indirect - github.com/docker/docker v23.0.0+incompatible // indirect + github.com/docker/cli v23.0.1+incompatible // indirect + github.com/docker/docker v23.0.1+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.4.3 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -41,16 +41,16 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/mod v0.9.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.5.0 // indirect - google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/tools v0.7.0 // indirect + google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.29.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9289060..ae5bf23 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c h1:/atD4G3jmfvsWfdtUePUs86Hr8qkOqC9Z8b4rTKj12A= github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c/go.mod h1:HFtyW133MGuoPtJLMUmSgdVihhzoDUJla9/S4TTyP74= -github.com/OpenSlides/vote-decrypt v0.0.0-20230304083156-533a12cfc9f0 h1:fuSvGtCmfMcXVJjCykz9b7/vslJzETxyXRkN+B84dYU= -github.com/OpenSlides/vote-decrypt v0.0.0-20230304083156-533a12cfc9f0/go.mod h1:0kJ4rcip5R12bO6wFBVxmdLq2VLbsisK+aIrbzH1FpU= +github.com/OpenSlides/vote-decrypt v0.0.0-20230314183154-52d9ebb2fd19 h1:MutnFtwJUUQPUxNZ42RygEn1OnMHA0BY+XRkPGZrcA8= +github.com/OpenSlides/vote-decrypt v0.0.0-20230314183154-52d9ebb2fd19/go.mod h1:JTLPgPy0efqp3YfTmkntQejCslhz4vypHVtYP/d/Lsg= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4= github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= @@ -28,10 +28,10 @@ github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/cli v23.0.0+incompatible h1:bcM4syaQ+EM/iczJTimMOGzvnzJBFPFEf4acS7sZ+RM= -github.com/docker/cli v23.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v23.0.0+incompatible h1:L6c28tNyqZ4/ub9AZC9d5QUuunoHHfEH4/Ue+h/E5nE= -github.com/docker/docker v23.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= +github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= +github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -43,11 +43,11 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -128,19 +128,19 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -159,32 +159,32 @@ golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= -golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 h1:QQF+HdiI4iocoxUjjpLgvTYDHKm99C/VtTBFnfiCJos= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From ca9c7fbef07cf218e42037deb43be4bf6d665e6e Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 15 Mar 2023 14:53:18 +0100 Subject: [PATCH 05/13] Make code compatible with current code --- internal/vote/http.go | 23 ++++++++++------------- internal/vote/http_test.go | 6 +++--- internal/vote/vote.go | 11 +++-------- internal/vote/vote_test.go | 26 +++++++++++++------------- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/internal/vote/http.go b/internal/vote/http.go index 5c3f400..9068976 100644 --- a/internal/vote/http.go +++ b/internal/vote/http.go @@ -91,6 +91,11 @@ func handleStart(mux *http.ServeMux, start starter) { return } + if pubkey == nil && pubKeySig == nil { + // Early exit for non crypto polls. + return + } + content := struct { PubKey []byte `json:"public_key"` PubKeySig []byte `json:"public_key_sig"` @@ -136,25 +141,17 @@ func handleStop(mux *http.ServeMux, stop stopper) { return } - // Encode the votes object separatly to make it possible for the - // backend (python) to read its original value. - encodedVotes, err := json.Marshal(stopResult.Votes) - if err != nil { - handleError(w, fmt.Errorf("encoding votes: %w", err), true) - return - } - if stopResult.UserIDs == nil { stopResult.UserIDs = []int{} } out := struct { - Votes string `json:"votes"` - Signature []byte `json:"signature"` - Users []int `json:"user_ids"` - Invalid map[int]string `json:"invalid,omitempty"` + Votes []json.RawMessage `json:"votes"` // TODO: the type has to be string. + Signature []byte `json:"signature,omitempty"` + Users []int `json:"user_ids"` + Invalid map[int]string `json:"invalid,omitempty"` }{ - string(encodedVotes), + stopResult.Votes, stopResult.Signature, stopResult.UserIDs, stopResult.Invalid, diff --git a/internal/vote/http_test.go b/internal/vote/http_test.go index c94f295..08dce3b 100644 --- a/internal/vote/http_test.go +++ b/internal/vote/http_test.go @@ -126,7 +126,7 @@ type stopperStub struct { id int expectErr error - expectVotes json.RawMessage + expectVotes []json.RawMessage expectedSignature []byte expectedUserIDs []int } @@ -171,7 +171,7 @@ func TestHandleStop(t *testing.T) { }) t.Run("Valid", func(t *testing.T) { - stopper.expectVotes = []byte(`"some values"`) + stopper.expectVotes = []json.RawMessage{([]byte(`"some values"`))} resp := httptest.NewRecorder() mux.ServeHTTP(resp, httptest.NewRequest("POST", url+"?id=1", nil)) @@ -184,7 +184,7 @@ func TestHandleStop(t *testing.T) { t.Errorf("Stopper was called with id %d, expected 1", stopper.id) } - expect := `{"votes":"\"some values\"","signature":null,"user_ids":[]}` + expect := `{"votes":["some values"],"user_ids":[]}` if trimed := strings.TrimSpace(resp.Body.String()); trimed != expect { t.Errorf("Got body `%s`, expected `%s`", trimed, expect) } diff --git a/internal/vote/vote.go b/internal/vote/vote.go index 5f5e658..c97b4fc 100644 --- a/internal/vote/vote.go +++ b/internal/vote/vote.go @@ -124,7 +124,7 @@ func (v *Vote) Start(ctx context.Context, pollID int) (pubkey []byte, pubKeySig // StopResult is the return value from vote.Stop. type StopResult struct { - Votes json.RawMessage + Votes []json.RawMessage // TODO: This has to be a string so the backend can validate the signature. Signature []byte UserIDs []int Invalid map[int]string @@ -168,12 +168,7 @@ func stopNonCrypto(ballots [][]byte, userIDs []int) (StopResult, error) { encodable[i] = ballots[i] } - votes, err := json.Marshal(encodable) - if err != nil { - return StopResult{}, fmt.Errorf("encode votes to list: %w", err) - } - - return StopResult{Votes: votes, UserIDs: userIDs}, nil + return StopResult{Votes: encodable, UserIDs: userIDs}, nil } func (v *Vote) stopCrypto(ctx context.Context, poll pollConfig, ds *dsfetch.Fetch, ballots [][]byte, userIDs []int) (StopResult, error) { @@ -217,7 +212,7 @@ func (v *Vote) stopCrypto(ctx context.Context, poll pollConfig, ds *dsfetch.Fetc } } - return StopResult{Votes: decrypted, Signature: signature, UserIDs: userIDs, Invalid: invalid}, nil + return StopResult{Votes: []json.RawMessage{decrypted}, Signature: signature, UserIDs: userIDs, Invalid: invalid}, nil } // Clear removes all knowlage of a poll. diff --git a/internal/vote/vote_test.go b/internal/vote/vote_test.go index 071dc2f..95f19e1 100644 --- a/internal/vote/vote_test.go +++ b/internal/vote/vote_test.go @@ -144,7 +144,6 @@ func TestVoteStart(t *testing.T) { v := vote.New(backend, backend, &ds, new(decrypterStub)) _, _, err := v.Start(context.Background(), 1) - if err != nil { t.Errorf("Start returned: %v", err) } @@ -272,14 +271,16 @@ func TestVoteStop(t *testing.T) { }) t.Run("Known poll", func(t *testing.T) { - if err := backend.Start(context.Background(), 2); err != nil { + ctx := context.Background() + + if err := backend.Start(ctx, 2); err != nil { t.Fatalf("Start returned an unexpected error: %v", err) } - backend.Vote(context.Background(), 2, 1, []byte(`{"value":"polldata1"}`)) - backend.Vote(context.Background(), 2, 2, []byte(`{"value":"polldata2"}`)) + backend.Vote(ctx, 2, 1, []byte(`{"value":"polldata1"}`)) + backend.Vote(ctx, 2, 2, []byte(`{"value":"polldata2"}`)) - stopResult, err := v.Stop(context.Background(), 2) + stopResult, err := v.Stop(ctx, 2) if err != nil { t.Fatalf("Stop returned unexpected error: %v", err) } @@ -287,16 +288,16 @@ func TestVoteStop(t *testing.T) { votes := stopResult.Votes userIDs := stopResult.UserIDs - expected := `[{"value":"polldata1"},{"value":"polldata2"}]` - if string(votes) != expected { - t.Errorf("Got votes %s, expected %s", votes, expected) + expected := []json.RawMessage{[]byte(`{"value":"polldata1"}`), []byte(`{"value":"polldata2"}`)} + if !reflect.DeepEqual(votes, expected) { + t.Errorf("Got\n%s\nexpected\n%s", votes, expected) } if !reflect.DeepEqual(userIDs, []int{1, 2}) { t.Errorf("Got users %v, expected [1 2]", userIDs) } - err = backend.Vote(context.Background(), 2, 3, []byte(`"polldata3"`)) + err = backend.Vote(ctx, 2, 3, []byte(`"polldata3"`)) var errStopped interface{ Stopped() } if !errors.As(err, &errStopped) { t.Errorf("Stop did not stop the poll in the backend.") @@ -316,7 +317,7 @@ func TestVoteStop(t *testing.T) { votes := stopResult.Votes userIDs := stopResult.UserIDs - if string(votes) != `[]` { + if len(votes) != 0 { t.Errorf("Got votes %s, expected []", votes) } @@ -356,7 +357,7 @@ func TestVoteStopCrypto(t *testing.T) { } expected := vote.StopResult{ - Votes: []byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"N"}]}`), + Votes: []json.RawMessage{[]byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"N"}]}`)}, Signature: []byte("signature"), UserIDs: []int{1, 2}, Invalid: map[int]string{}, @@ -397,7 +398,7 @@ func TestVoteStopCryptoInvalid(t *testing.T) { } expected := vote.StopResult{ - Votes: []byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"Invalid"}]}`), + Votes: []json.RawMessage{[]byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"Invalid"}]}`)}, Signature: []byte("signature"), UserIDs: []int{1, 2}, Invalid: map[int]string{1: "Global vote Invalid is not enabled"}, @@ -477,7 +478,6 @@ func TestVoteVote(t *testing.T) { if err != nil { t.Fatalf("Vote returned unexpected error: %v", err) } - }) t.Run("User has voted", func(t *testing.T) { From f23c8927a451e8d5c94bf20bc125df85905739f5 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 15 Mar 2023 16:04:15 +0100 Subject: [PATCH 06/13] Disable vote-decrypt per default --- main.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index eeb2c26..cb2cda3 100644 --- a/main.go +++ b/main.go @@ -39,7 +39,7 @@ var ( envPostgresDatabase = environment.NewVariable("VOTE_DATABASE_NAME", "openslides", "") envPostgresPassword = environment.NewSecret("postgres_password", "Password of the postgres database used for long polls.") - envVoteDecryptService = environment.NewVariable("VOTE_DECRYPT_SERVICE", "localhost:9014", "Host and port of the decrypt service.") + envVoteDecryptService = environment.NewVariable("VOTE_DECRYPT_SERVICE", "", "Host and port of the decrypt service. Empty string to disable this feature.") ) var cli struct { @@ -147,11 +147,15 @@ func initService(lookup environment.Environmenter) (func(context.Context) error, return fmt.Errorf("start long backend: %w", err) } - decrypter, close, err := grpc.NewClient(decryptAddr) - if err != nil { - return fmt.Errorf("connection to vote decrypt service via grpc: %w", err) + var decrypter *grpc.Client + if decryptAddr != "" { + decr, close, err := grpc.NewClient(decryptAddr) + if err != nil { + return fmt.Errorf("connection to vote decrypt service via grpc: %w", err) + } + defer close() + decrypter = decr } - defer close() voteService := vote.New(fastBackend, longBackend, datastoreService, decrypter) From d85b4f3a9d89164b352bdea355e5afb8161a8f0b Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 15 Mar 2023 16:29:33 +0100 Subject: [PATCH 07/13] Fix error message for backend --- internal/vote/vote.go | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/vote/vote.go b/internal/vote/vote.go index c97b4fc..5d62f98 100644 --- a/internal/vote/vote.go +++ b/internal/vote/vote.go @@ -402,7 +402,7 @@ func ensureVoteUser(ctx context.Context, ds *dsfetch.Fetch, poll pollConfig, vot } if !equalElement(groupIDs, poll.groups) { - return MessageError{ErrNotAllowed, fmt.Sprintf("User %d is not in a group that is allowed to vote", voteUser)} + return MessageError{ErrNotAllowed, fmt.Sprintf("User %d is not allowed to vote. He is not in an entitled group", voteUser)} } if voteUser == requestUser { diff --git a/main.go b/main.go index cb2cda3..c790a9a 100644 --- a/main.go +++ b/main.go @@ -147,7 +147,7 @@ func initService(lookup environment.Environmenter) (func(context.Context) error, return fmt.Errorf("start long backend: %w", err) } - var decrypter *grpc.Client + var decrypter vote.Decrypter if decryptAddr != "" { decr, close, err := grpc.NewClient(decryptAddr) if err != nil { From 59abbde6f7fdd2bac0091b9f4ec9eadde3ffd08e Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 18 Mar 2023 07:25:20 +0100 Subject: [PATCH 08/13] Update deps and generated --- environment.md | 4 ++-- go.mod | 4 ++-- go.sum | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/environment.md b/environment.md index 76d2ac0..ac9ed04 100644 --- a/environment.md +++ b/environment.md @@ -23,10 +23,10 @@ The Service uses the following environment variables: * `VOTE_DATABASE_USER`: Databasename of the postgres database used for long polls. The default is `openslides`. * `VOTE_DATABASE_HOST`: Host of the postgres database used for long polls. The default is `localhost`. * `VOTE_DATABASE_PORT`: Port of the postgres database used for long polls. The default is `5432`. -* `VOTE_DATABASE_NAME`: The default is `openslides`. +* `VOTE_DATABASE_NAME`: Name of the database to save long running polls. The default is `openslides`. * `VOTE_BACKEND_FAST`: The backend used for fast polls. Possible backends are redis, postgres or memory. The default is `redis`. * `VOTE_BACKEND_LONG`: The backend used for long polls. The default is `postgres`. -* `VOTE_DECRYPT_SERVICE`: Host and port of the decrypt service. The default is `localhost:9014`. +* `VOTE_DECRYPT_SERVICE`: Host and port of the decrypt service. Empty string to disable this feature. The default is ``. ## Secrets diff --git a/go.mod b/go.mod index 17cd8c0..6196b62 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c - github.com/OpenSlides/vote-decrypt v0.0.0-20230314183154-52d9ebb2fd19 + github.com/OpenSlides/vote-decrypt v0.0.0-20230317070656-9af51d5a96d2 github.com/alecthomas/kong v0.7.1 github.com/gomodule/redigo v1.8.9 github.com/jackc/pgx/v5 v5.3.1 @@ -50,7 +50,7 @@ require ( golang.org/x/tools v0.7.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.29.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fb17a2d..3efa7ec 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c h1:/atD4G3jmfvsWfdtUePUs86Hr8qkOqC9Z8b4rTKj12A= github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230205113522-64e8a845d31c/go.mod h1:HFtyW133MGuoPtJLMUmSgdVihhzoDUJla9/S4TTyP74= -github.com/OpenSlides/vote-decrypt v0.0.0-20230314183154-52d9ebb2fd19 h1:MutnFtwJUUQPUxNZ42RygEn1OnMHA0BY+XRkPGZrcA8= -github.com/OpenSlides/vote-decrypt v0.0.0-20230314183154-52d9ebb2fd19/go.mod h1:JTLPgPy0efqp3YfTmkntQejCslhz4vypHVtYP/d/Lsg= +github.com/OpenSlides/vote-decrypt v0.0.0-20230317070656-9af51d5a96d2 h1:tlJ97Ia31o/fDTO1mjF7BsZ46ejplnfrkxIine+RLXI= +github.com/OpenSlides/vote-decrypt v0.0.0-20230317070656-9af51d5a96d2/go.mod h1:L6bLLrlWthJftcn8nIdHslpQzBrD0V8c+a8KEFyU1bw= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4= github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= @@ -183,8 +183,8 @@ google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwS google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From e09669c10f7246c1d3e1adac939ff682a54e955d Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 18 Mar 2023 07:39:23 +0100 Subject: [PATCH 09/13] Fix vote return format --- internal/vote/http.go | 16 +++++----------- internal/vote/http_test.go | 6 +++--- internal/vote/vote.go | 20 +++++++++++++++++--- internal/vote/vote_test.go | 12 ++++++------ 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/internal/vote/http.go b/internal/vote/http.go index 60b46e5..3d6b572 100644 --- a/internal/vote/http.go +++ b/internal/vote/http.go @@ -141,23 +141,17 @@ func handleStop(mux *http.ServeMux, stop stopper) { return } - // Convert vote objects to json.RawMessage - encodableObjects := make([]json.RawMessage, len(result.Votes)) - for i := range result.Votes { - encodableObjects[i] = result.Votes[i] - } - if result.UserIDs == nil { result.UserIDs = []int{} } out := struct { - Votes []json.RawMessage `json:"votes"` // TODO: the type has to be string. - Signature []byte `json:"signature,omitempty"` - Users []int `json:"user_ids"` - Invalid map[int]string `json:"invalid,omitempty"` + Votes string `json:"votes"` + Signature []byte `json:"signature,omitempty"` + Users []int `json:"user_ids"` + Invalid map[int]string `json:"invalid,omitempty"` }{ - encodableObjects, + result.Votes, result.Signature, result.UserIDs, result.Invalid, diff --git a/internal/vote/http_test.go b/internal/vote/http_test.go index 5af60c0..d24f6f5 100644 --- a/internal/vote/http_test.go +++ b/internal/vote/http_test.go @@ -126,7 +126,7 @@ type stopperStub struct { id int expectErr error - expectedVotes [][]byte + expectedVotes string expectedSignature []byte expectedUserIDs []int } @@ -171,7 +171,7 @@ func TestHandleStop(t *testing.T) { }) t.Run("Valid", func(t *testing.T) { - stopper.expectedVotes = [][]byte{[]byte(`"some values"`)} + stopper.expectedVotes = `["some values"]` resp := httptest.NewRecorder() mux.ServeHTTP(resp, httptest.NewRequest("POST", url+"?id=1", nil)) @@ -184,7 +184,7 @@ func TestHandleStop(t *testing.T) { t.Errorf("Stopper was called with id %d, expected 1", stopper.id) } - expect := `{"votes":["some values"],"user_ids":[]}` + expect := `{"votes":"[\"some values\"]","user_ids":[]}` if trimed := strings.TrimSpace(resp.Body.String()); trimed != expect { t.Errorf("Got body:\n`%s`, expected:\n`%s`", trimed, expect) } diff --git a/internal/vote/vote.go b/internal/vote/vote.go index 4a27731..affee1c 100644 --- a/internal/vote/vote.go +++ b/internal/vote/vote.go @@ -119,7 +119,7 @@ func (v *Vote) Start(ctx context.Context, pollID int) (pubkey, pubKeySig []byte, // StopResult is the return value from vote.Stop. type StopResult struct { - Votes [][]byte // TODO: This has to be a string so the backend can validate the signature. + Votes string Signature []byte UserIDs []int Invalid map[int]string @@ -151,10 +151,24 @@ func (v *Vote) Stop(ctx context.Context, pollID int) (StopResult, error) { case "cryptographic": return v.stopCrypto(ctx, poll, ds, ballots, userIDs) default: - return StopResult{Votes: ballots, UserIDs: userIDs}, nil + return v.stopNonCrypt(ballots, userIDs) } } +func (v *Vote) stopNonCrypt(ballots [][]byte, userIDs []int) (StopResult, error) { + encodable := make([]json.RawMessage, len(ballots)) + for i := range ballots { + encodable[i] = ballots[i] + } + + votes, err := json.Marshal(encodable) + if err != nil { + return StopResult{}, fmt.Errorf("encode votes to list: %w", err) + } + + return StopResult{Votes: string(votes), UserIDs: userIDs}, nil +} + func (v *Vote) stopCrypto(ctx context.Context, poll pollConfig, ds *dsfetch.Fetch, ballots [][]byte, userIDs []int) (StopResult, error) { qid, err := v.qualifiedID(ctx, ds, poll.id) if err != nil { @@ -196,7 +210,7 @@ func (v *Vote) stopCrypto(ctx context.Context, poll pollConfig, ds *dsfetch.Fetc } } - return StopResult{Votes: [][]byte{decrypted}, Signature: signature, UserIDs: userIDs, Invalid: invalid}, nil + return StopResult{Votes: string(decrypted), Signature: signature, UserIDs: userIDs, Invalid: invalid}, nil } // Clear removes all knowlage of a poll. diff --git a/internal/vote/vote_test.go b/internal/vote/vote_test.go index 9b4dcff..84c2945 100644 --- a/internal/vote/vote_test.go +++ b/internal/vote/vote_test.go @@ -287,8 +287,8 @@ func TestVoteStop(t *testing.T) { votes := stopResult.Votes userIDs := stopResult.UserIDs - expected := [][]byte{[]byte(`{"value":"polldata1"}`), []byte(`{"value":"polldata2"}`)} - if !reflect.DeepEqual(votes, expected) { + expected := `[{"value":"polldata1"},{"value":"polldata2"}]` + if votes != expected { t.Errorf("Got\n`%s`, expected\n`%s`", votes, expected) } @@ -317,8 +317,8 @@ func TestVoteStop(t *testing.T) { votes := stopResult.Votes userIDs := stopResult.UserIDs - if len(votes) != 0 { - t.Errorf("Got votes %s, expected []", votes) + if votes != `[]` { + t.Errorf("Got votes `%s`, expected `[]`", votes) } if len(userIDs) != 0 { @@ -357,7 +357,7 @@ func TestVoteStopCrypto(t *testing.T) { } expected := vote.StopResult{ - Votes: [][]byte{[]byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"N"}]}`)}, + Votes: `{"id":"/1","votes":[{"votes":"Y"},{"votes":"N"}]}`, Signature: []byte("signature"), UserIDs: []int{1, 2}, Invalid: map[int]string{}, @@ -398,7 +398,7 @@ func TestVoteStopCryptoInvalid(t *testing.T) { } expected := vote.StopResult{ - Votes: [][]byte{[]byte(`{"id":"/1","votes":[{"votes":"Y"},{"votes":"Invalid"}]}`)}, + Votes: `{"id":"/1","votes":[{"votes":"Y"},{"votes":"Invalid"}]}`, Signature: []byte("signature"), UserIDs: []int{1, 2}, Invalid: map[int]string{1: "Global vote Invalid is not enabled"}, From 7b6335b19276fc13864486257bf7df62a73f9031 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sun, 21 May 2023 18:02:46 +0200 Subject: [PATCH 10/13] Update generated files --- environment.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/environment.md b/environment.md index 0738d8c..a900394 100644 --- a/environment.md +++ b/environment.md @@ -9,25 +9,23 @@ The Service uses the following environment variables: * `MESSAGE_BUS_HOST`: Host of the redis server. The default is `localhost`. * `MESSAGE_BUS_PORT`: Port of the redis server. The default is `6379`. * `DATASTORE_DATABASE_USER`: Postgres User. The default is `openslides`. +* `OPENSLIDES_DEVELOPMENT`: If set, the service uses the default secrets. The default is `false`. +* `SECRETS_PATH`: Path where the secrets are stored. The default is `/run/secrets`. * `DATASTORE_DATABASE_HOST`: Postgres Host. The default is `localhost`. * `DATASTORE_DATABASE_PORT`: Postgres Post. The default is `5432`. * `DATASTORE_DATABASE_NAME`: Postgres Database. The default is `openslides`. -* `OPENSLIDES_DEVELOPMENT`: If set, the service uses the default secrets. The default is `false`. -* `SECRETS_PATH`: Path where the secrets are stored. The default is `/run/secrets`. * `AUTH_PROTOCOL`: Protocol of the auth service. The default is `http`. * `AUTH_HOST`: Host of the auth service. The default is `localhost`. * `AUTH_PORT`: Port of the auth service. The default is `9004`. -* `AUTH_Fake`: Use user id 1 for every request. Ignores all other auth environment variables. The default is `false`. +* `AUTH_FAKE`: Use user id 1 for every request. Ignores all other auth environment variables. The default is `false`. * `VOTE_REDIS_HOST`: Host of the redis used for the fast backend. The default is `localhost`. * `VOTE_REDIS_PORT`: Port of the redis used for the fast backend. The default is `6379`. * `VOTE_DATABASE_USER`: Databasename of the postgres database used for long polls. The default is `openslides`. * `VOTE_DATABASE_HOST`: Host of the postgres database used for long polls. The default is `localhost`. * `VOTE_DATABASE_PORT`: Port of the postgres database used for long polls. The default is `5432`. * `VOTE_DATABASE_NAME`: Name of the database to save long running polls. The default is `openslides`. -* `VOTE_BACKEND_FAST`: The backend used for fast polls. Possible backends are redis, postgres or memory. The default is `redis`. -* `VOTE_BACKEND_LONG`: The backend used for long polls. The default is `postgres`. -* `VOTE_DECRYPT_SERVICE`: Host and port of the decrypt service. Empty string to disable this feature. The default is ``. * `VOTE_SINGLE_INSTANCE`: More performance if the serice is not scalled horizontally. The default is `false`. +* `VOTE_DECRYPT_SERVICE`: Host and port of the decrypt service. Empty string to disable this feature. The default is ``. ## Secrets From fe05c1d5ada5c86e60f1cf2aeb1a108efb4db015 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Wed, 14 Jun 2023 16:45:13 +0200 Subject: [PATCH 11/13] Update autoupdate --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index fcbfd64..ef263e5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/OpenSlides/openslides-vote-service go 1.20 require ( - github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230519212348-5aab6b37920d + github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230614144349-b7250a3c0e45 github.com/OpenSlides/vote-decrypt v0.0.0-20230510045515-df8672d3571c github.com/alecthomas/kong v0.7.1 github.com/gomodule/redigo v1.8.9 @@ -45,7 +45,7 @@ require ( golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/tools v0.9.1 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/go.sum b/go.sum index 5e31321..378ee7e 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230519212348-5aab6b37920d h1:tJN8vaAh0SkOm042VVCVUKAbZN7nqUjPmgujOmcbQgk= -github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230519212348-5aab6b37920d/go.mod h1:FW5vdHOPhO+77mC11wEnJQX/spfwsLIGAi0JdzEygTc= +github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230614144349-b7250a3c0e45 h1:ztqvYPUPmbGdWl4OCZRhK0qlxOSEOfvA4SeAFqpUMTQ= +github.com/OpenSlides/openslides-autoupdate-service v0.4.1-0.20230614144349-b7250a3c0e45/go.mod h1:R7dQCWjeYdhtOAiz0PjFbgLU8NBuGU4tcDuX74qGCnA= github.com/OpenSlides/vote-decrypt v0.0.0-20230510045515-df8672d3571c h1:0v3VQIiU1kA8kXSmn0eNS5M5Ktaje6nMjQBhMyWG2sk= github.com/OpenSlides/vote-decrypt v0.0.0-20230510045515-df8672d3571c/go.mod h1:/Xh6ksrMaK3yiT1DiaZL9emvF17mTN210MpazkAxe3M= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= @@ -87,7 +87,7 @@ github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -122,8 +122,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= From ff122601e033c7bb8877e95e79e4da09794674a0 Mon Sep 17 00:00:00 2001 From: Magnus Schieder Date: Tue, 4 Jun 2024 17:47:51 +0200 Subject: [PATCH 12/13] Fix TestStartVoteStopClear --- system_test/system_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system_test/system_test.go b/system_test/system_test.go index f632243..11f8b30 100644 --- a/system_test/system_test.go +++ b/system_test/system_test.go @@ -63,7 +63,7 @@ func TestStartVoteStopClear(t *testing.T) { t.Fatalf("Stop poll: %v", err) } - expectBody := `{"votes":[{"request_user_id":1,"vote_user_id":1,"value":"Y","weight":"1.000000"}],"user_ids":[1]}` + expectBody := `{"votes":"[{\"request_user_id\":1,\"vote_user_id\":1,\"value\":\"Y\",\"weight\":\"1.000000\"}]","user_ids":[1]}` if strings.TrimSpace(string(stopBody)) != expectBody { t.Fatalf("Got != expect\n%s\n%s", stopBody, expectBody) } From a90eff40c447b00321ae9fa4de7abcbb6e9c22a7 Mon Sep 17 00:00:00 2001 From: Magnus Schieder Date: Mon, 10 Jun 2024 14:04:16 +0200 Subject: [PATCH 13/13] Fix test --- system_test/system_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/system_test/system_test.go b/system_test/system_test.go index 11f8b30..ee0f2f9 100644 --- a/system_test/system_test.go +++ b/system_test/system_test.go @@ -115,6 +115,7 @@ func startPoll(ctx context.Context, db *postgresTestData, pollID int) error { is_present_in_meeting_ids: [1] meeting_user_ids: [10] meeting/1/id: 5 + organization/1/url: test `, pollID)))