From 567755cca6659cec9fcc3607e118556045a88225 Mon Sep 17 00:00:00 2001 From: Ella S <112343747+pikachu0542@users.noreply.github.com> Date: Mon, 5 May 2025 21:27:04 -0400 Subject: [PATCH 1/9] Removed hard coding of candidate names --- database/poll.go | 1 - 1 file changed, 1 deletion(-) diff --git a/database/poll.go b/database/poll.go index b85f62e..699d95f 100644 --- a/database/poll.go +++ b/database/poll.go @@ -214,7 +214,6 @@ func (poll *Poll) GetResult(ctx context.Context) ([]map[string]int, error) { case POLL_TYPE_RANKED: // We want to store those that were eliminated eliminated := make([]string, 0) - eliminated = append(eliminated, "Isaac Ingram", "Charlotte George") // Get all votes cursor, err := Client.Database(db).Collection("votes").Aggregate(ctx, mongo.Pipeline{ From 65be89835d011e1cc0c152cdd0912d42b8f086ea Mon Sep 17 00:00:00 2001 From: Cole Stowell Date: Mon, 5 May 2025 20:24:44 -0400 Subject: [PATCH 2/9] feat: hidden template --- README.md | 1 + main.go | 5 ++++- templates/hidden.tmpl | 50 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 templates/hidden.tmpl diff --git a/README.md b/README.md index 9a8b259..ae35812 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ You'll need to set up these values in your environment. Ask an RTP for OIDC cred ``` VOTE_HOST=http://localhost:8080 VOTE_JWT_SECRET= +VOTE_MONGO_DB= VOTE_MONGODB_URI= VOTE_OIDC_ID=vote VOTE_OIDC_SECRET= diff --git a/main.go b/main.go index 5145343..a42fa90 100644 --- a/main.go +++ b/main.go @@ -334,7 +334,10 @@ func main() { } if poll.Hidden && poll.CreatedBy != claims.UserInfo.Username { - c.JSON(403, gin.H{"Success": "Result Hidden"}) + c.HTML(403, "hidden.tmpl", gin.H{ + "Username": claims.UserInfo.Username, + "FullName": claims.UserInfo.FullName, + }) return } diff --git a/templates/hidden.tmpl b/templates/hidden.tmpl new file mode 100644 index 0000000..e7a0969 --- /dev/null +++ b/templates/hidden.tmpl @@ -0,0 +1,50 @@ + + + + CSH Vote + + + + + + + + +
+ Attention! +
+

The results are hidden!

+

Looks like you aren't allowed to see the results.

+

+ Please contact the owner of this poll or a Root Type Person if you think + this is an error. +

+
+ + From 7b656cbd68c6f5a34d40f70513f5e3327a02a999 Mon Sep 17 00:00:00 2001 From: Cole Stowell Date: Tue, 6 May 2025 00:21:26 -0400 Subject: [PATCH 3/9] feat: validate ranked choice vote input --- main.go | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index a42fa90..4e371f5 100644 --- a/main.go +++ b/main.go @@ -280,15 +280,32 @@ func main() { PollId: pId, UserId: claims.UserInfo.Username, } + + voted := make([]bool, len(poll.Options)) + max_num := len(vote.Options) + for _, opt := range poll.Options { - if c.PostForm(opt) != "" { - rank, err := strconv.Atoi(c.PostForm(opt)) - if err != nil { - c.JSON(500, gin.H{"error": "error parsing votes"}) - } - if rank > 0 { - vote.Options[opt] = rank - } + if c.PostForm(opt) == "" { + c.JSON(400, gin.H{"error": "did not fill out all rankings"}) + return + } + rank, err := strconv.Atoi(c.PostForm(opt)) + if err != nil { + c.JSON(400, gin.H{"error": "non-number ranking"}) + return + } + if rank > 0 && rank <= max_num { + vote.Options[opt] = rank + voted[rank - 1] = true + } else { + c.JSON(400, gin.H{"error": fmt.Sprintf("votes must be from 1 - %d", max_num)}) + return + } + } + for num, vote := range voted { + if !vote { + c.JSON(400, gin.H{"error": fmt.Sprintf("no candidate ranked at #%d", num+1)}) + return } } if c.PostForm("writeinOption") != "" && c.PostForm("writein") != "" { From 354348c65d0a48739766764f095bfb933844695e Mon Sep 17 00:00:00 2001 From: Akash Keshav <112591754+domesticchores@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:02:26 -0400 Subject: [PATCH 4/9] prevent exact duplicates from being filled in --- main.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 4e371f5..caab495 100644 --- a/main.go +++ b/main.go @@ -3,19 +3,20 @@ package main import ( "encoding/json" "fmt" - cshAuth "github.com/computersciencehouse/csh-auth" - "github.com/computersciencehouse/vote/database" - "github.com/computersciencehouse/vote/sse" - "github.com/gin-gonic/gin" - "go.mongodb.org/mongo-driver/bson/primitive" "html/template" - "mvdan.cc/xurls/v2" "net/http" "os" "sort" "strconv" "strings" "time" + + cshAuth "github.com/computersciencehouse/csh-auth" + "github.com/computersciencehouse/vote/database" + "github.com/computersciencehouse/vote/sse" + "github.com/gin-gonic/gin" + "go.mongodb.org/mongo-driver/bson/primitive" + "mvdan.cc/xurls/v2" ) func inc(x int) string { @@ -309,6 +310,9 @@ func main() { } } if c.PostForm("writeinOption") != "" && c.PostForm("writein") != "" { + if vote.Options[c.PostForm("writeinOption")] != 0 { + c.JSON(500, gin.H{"error": "write-in is already an option"}) + } rank, err := strconv.Atoi(c.PostForm("writein")) if err != nil { c.JSON(500, gin.H{"error": "error parsing votes"}) From 181e3e94cefb0842cd5e410bf4fc3b5573ddfeb1 Mon Sep 17 00:00:00 2001 From: Akash Keshav <112591754+domesticchores@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:00:09 -0400 Subject: [PATCH 5/9] add return statement --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index caab495..ad86d98 100644 --- a/main.go +++ b/main.go @@ -312,6 +312,7 @@ func main() { if c.PostForm("writeinOption") != "" && c.PostForm("writein") != "" { if vote.Options[c.PostForm("writeinOption")] != 0 { c.JSON(500, gin.H{"error": "write-in is already an option"}) + return } rank, err := strconv.Atoi(c.PostForm("writein")) if err != nil { From 2faabd167df08f1763de970024d2ff4e2fd3623d Mon Sep 17 00:00:00 2001 From: Akash Keshav <112591754+domesticchores@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:23:24 -0400 Subject: [PATCH 6/9] added much better method that checks for spaces and case sensitivity --- main.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index ad86d98..ff357c8 100644 --- a/main.go +++ b/main.go @@ -310,10 +310,16 @@ func main() { } } if c.PostForm("writeinOption") != "" && c.PostForm("writein") != "" { - if vote.Options[c.PostForm("writeinOption")] != 0 { - c.JSON(500, gin.H{"error": "write-in is already an option"}) - return + for candidate := range vote.Options { + if strings.EqualFold(candidate, strings.TrimSpace(c.PostForm("writeinOption"))) { + c.JSON(500, gin.H{"error": "write-in is already an option"}) + return + } } + // if vote.Options[c.PostForm("writeinOption")] != 0 { + // c.JSON(500, gin.H{"error": "write-in is already an option"}) + // return + // } rank, err := strconv.Atoi(c.PostForm("writein")) if err != nil { c.JSON(500, gin.H{"error": "error parsing votes"}) From c8ffb850625b715988a37e86235c62d0c2b86e08 Mon Sep 17 00:00:00 2001 From: Akash Keshav <112591754+domesticchores@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:25:05 -0400 Subject: [PATCH 7/9] remove comments --- docker-compose.yaml | 3 ++- main.go | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 156c4a4..f9854c7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,9 +9,10 @@ services: VOTE_HOST: 'http://localhost:8080' VOTE_JWT_SECRET: 4874c601dda90a01c7543c571be08680 VOTE_MONGODB_URI: "mongodb://vote:c1f66aac6b4fafbef3c659371b8a50ed@mongodb/vote?authSource=admin" - VOTE_OIDC_ID: vote + VOTE_OIDC_ID: vote-dev VOTE_OIDC_SECRET: "${VOTE_OIDC_SECRET}" VOTE_STATE: 27a28540e47ec786b7bdad03f83171b3 + VOTE_MONGO_DB: "vote" ports: - "127.0.0.1:8080:8080" diff --git a/main.go b/main.go index ff357c8..3510b22 100644 --- a/main.go +++ b/main.go @@ -316,10 +316,6 @@ func main() { return } } - // if vote.Options[c.PostForm("writeinOption")] != 0 { - // c.JSON(500, gin.H{"error": "write-in is already an option"}) - // return - // } rank, err := strconv.Atoi(c.PostForm("writein")) if err != nil { c.JSON(500, gin.H{"error": "error parsing votes"}) From 1c0d31482f621f7118e3d5955692bdf688f4dfc9 Mon Sep 17 00:00:00 2001 From: Akash Keshav <112591754+domesticchores@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:09:40 -0400 Subject: [PATCH 8/9] revert docker compose changes --- docker-compose.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index f9854c7..156c4a4 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,10 +9,9 @@ services: VOTE_HOST: 'http://localhost:8080' VOTE_JWT_SECRET: 4874c601dda90a01c7543c571be08680 VOTE_MONGODB_URI: "mongodb://vote:c1f66aac6b4fafbef3c659371b8a50ed@mongodb/vote?authSource=admin" - VOTE_OIDC_ID: vote-dev + VOTE_OIDC_ID: vote VOTE_OIDC_SECRET: "${VOTE_OIDC_SECRET}" VOTE_STATE: 27a28540e47ec786b7bdad03f83171b3 - VOTE_MONGO_DB: "vote" ports: - "127.0.0.1:8080:8080" From 09466af52c3cc43b00af24ed0948448921f9cdd0 Mon Sep 17 00:00:00 2001 From: shaeespring Date: Sun, 5 Oct 2025 23:00:24 -0400 Subject: [PATCH 9/9] gatekeep --- main.go | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 3510b22..eb44b44 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "html/template" + "log" "net/http" "os" "sort" @@ -297,7 +298,7 @@ func main() { } if rank > 0 && rank <= max_num { vote.Options[opt] = rank - voted[rank - 1] = true + voted[rank-1] = true } else { c.JSON(400, gin.H{"error": fmt.Sprintf("votes must be from 1 - %d", max_num)}) return @@ -513,7 +514,7 @@ func main() { r.Run() } -func canVote(groups []string) bool { +func canVote(groups []string, username string) bool { var active, fallCoop, springCoop bool for _, group := range groups { if group == "active" { @@ -530,10 +531,38 @@ func canVote(groups []string) bool { } } + type Result struct { + Result bool `json:"result"` + } + + // gatekeep + gatekeepURL := "https://conditional.csh.rit.edu/gatekeep/" + username + voteToken := os.Getenv("VOTE_TOKEN") + req, err := http.NewRequest("GET", gatekeepURL, nil) + if err != nil { + log.Fatal(err) + return false + } + req.Header.Add("X-VOTE-TOKEN", voteToken) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Fatal(err) + return false + } + defer resp.Body.Close() + + var result Result + err = json.NewDecoder(resp.Body).Decode(&result) + if err != nil { + log.Fatal(err) + return false + } + gatekeep := result.Result if time.Now().Month() > time.July { - return active && !fallCoop + return active && !fallCoop && gatekeep } else { - return active && !springCoop + return active && !springCoop && gatekeep } }