From fd575f94d137bfcc464109b72c3b1cc141631ccb Mon Sep 17 00:00:00 2001 From: Tyler Allen Date: Thu, 5 Mar 2026 13:55:34 -0500 Subject: [PATCH 1/6] eboard vote --- README.md | 1 + docker-compose.yaml | 1 + main.go | 62 ++++++++++++++++++------------------------- templates/create.tmpl | 25 +++++++++++++---- templates/result.tmpl | 2 +- users.go | 24 +++++++++++++++++ 6 files changed, 73 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index e3ae5f0..eb2dd14 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ VOTE_SLACK_BOT_TOKEN= ### Dev Overrides `DEV_DISABLE_ACTIVE_FILTERS="true"` will disable the requirements that you be active to vote `DEV_FORCE_IS_EVALS="true"` will force vote to treat all users as the Evals director +`DEV_FORCE_IS_CHAIR="true"` will force vote to treat all users as the Chairperson ## Linting These will be checked by CI diff --git a/docker-compose.yaml b/docker-compose.yaml index 4902a92..598d57a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -22,6 +22,7 @@ services: VOTE_STATE: 27a28540e47ec786b7bdad03f83171b3 DEV_DISABLE_ACTIVE_FILTERS: "${DEV_DISABLE_ACTIVE_FILTERS}" DEV_FORCE_IS_EVALS: "${DEV_FORCE_IS_EVALS}" + DEV_FORCE_IS_CHAIR: "${DEV_FORCE_IS_CHAIR}" ports: - "127.0.0.1:8080:8080" diff --git a/main.go b/main.go index dba0cb7..363884c 100644 --- a/main.go +++ b/main.go @@ -30,19 +30,20 @@ var CONDITIONAL_GATEKEEP_URL = os.Getenv("VOTE_CONDITIONAL_URL") var VOTE_HOST = os.Getenv("VOTE_HOST") // Dev mode flags -var DEV_DISABLE_ACTIVE_FILTERS bool = os.Getenv("DEV_DISABLE_ACTIVE_FILTERS") == "true" -var DEV_FORCE_IS_EVALS bool = os.Getenv("DEV_FORCE_IS_EVALS") == "true" +var DEV_DISABLE_ACTIVE_FILTERS = os.Getenv("DEV_DISABLE_ACTIVE_FILTERS") == "true" +var DEV_FORCE_IS_EVALS = os.Getenv("DEV_FORCE_IS_EVALS") == "true" +var DEV_FORCE_IS_CHAIR = os.Getenv("DEV_FORCE_IS_CHAIR") == "true" func inc(x int) string { return strconv.Itoa(x + 1) } -// Gets the number of people eligible to vote in a poll +// GetVoterCount Gets the number of people eligible to vote in a poll func GetVoterCount(poll database.Poll) int { return len(poll.AllowedUsers) } -// Calculates the number of votes required for quorum in a poll +// CalculateQuorum Calculates the number of votes required for quorum in a poll func CalculateQuorum(poll database.Poll) int { voterCount := GetVoterCount(poll) return int(math.Ceil(float64(voterCount) * poll.QuorumType)) @@ -111,23 +112,6 @@ func main() { return polls[i].Id > polls[j].Id }) - closedPolls, err := database.GetClosedVotedPolls(c, claims.UserInfo.Username) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - ownedPolls, err := database.GetClosedOwnedPolls(c, claims.UserInfo.Username) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - closedPolls = append(closedPolls, ownedPolls...) - - sort.Slice(closedPolls, func(i, j int) bool { - return closedPolls[i].Id > closedPolls[j].Id - }) - closedPolls = uniquePolls(closedPolls) - c.HTML(http.StatusOK, "index.tmpl", gin.H{ "Polls": polls, "Username": claims.UserInfo.Username, @@ -216,8 +200,19 @@ func main() { AllowWriteIns: c.PostForm("allowWriteIn") == "true", Hidden: c.PostForm("hidden") == "true", } - if c.PostForm("rankedChoice") == "true" { + switch c.PostForm("pollType") { + case "rankedChoice": poll.VoteType = database.POLL_TYPE_RANKED + case "eboard": + eboard := oidcClient.GetEBoard() + var usernames []string + for _, member := range eboard { + usernames = append(usernames, member.Username) + } + poll.AllowedUsers = usernames + poll.AllowWriteIns = false + poll.Hidden = true + poll.Gatekeep = false } switch c.PostForm("options") { @@ -229,7 +224,7 @@ func main() { poll.Options = []string{} for opt := range strings.SplitSeq(c.PostForm("customOptions"), ",") { poll.Options = append(poll.Options, strings.TrimSpace(opt)) - if !containsString(poll.Options, "Abstain") && (poll.VoteType == database.POLL_TYPE_SIMPLE) { + if !slices.Contains(poll.Options, "Abstain") && (poll.VoteType == database.POLL_TYPE_SIMPLE) { poll.Options = append(poll.Options, "Abstain") } } @@ -283,7 +278,7 @@ func main() { writeInAdj = 1 } - canModify := containsString(claims.UserInfo.Groups, "active_rtp") || containsString(claims.UserInfo.Groups, "eboard") || poll.CreatedBy == claims.UserInfo.Username + canModify := slices.Contains(claims.UserInfo.Groups, "active_rtp") || slices.Contains(claims.UserInfo.Groups, "eboard") || poll.CreatedBy == claims.UserInfo.Username c.HTML(200, "poll.tmpl", gin.H{ "Id": poll.Id, @@ -448,7 +443,7 @@ func main() { return } - canModify := containsString(claims.UserInfo.Groups, "active_rtp") || containsString(claims.UserInfo.Groups, "eboard") || poll.CreatedBy == claims.UserInfo.Username + canModify := slices.Contains(claims.UserInfo.Groups, "active_rtp") || slices.Contains(claims.UserInfo.Groups, "eboard") || poll.CreatedBy == claims.UserInfo.Username votesNeededForQuorum := int(poll.QuorumType * float64(len(poll.AllowedUsers))) c.HTML(http.StatusOK, "result.tmpl", gin.H{ @@ -525,7 +520,7 @@ func main() { } if poll.CreatedBy != claims.UserInfo.Username { - if containsString(claims.UserInfo.Groups, "active_rtp") || containsString(claims.UserInfo.Groups, "eboard") { + if slices.Contains(claims.UserInfo.Groups, "active_rtp") || slices.Contains(claims.UserInfo.Groups, "eboard") { } else { c.JSON(http.StatusForbidden, gin.H{"error": "You cannot end this poll."}) return @@ -563,7 +558,11 @@ func main() { // isEvals determines if the current user is evals, allowing for a dev mode override func isEvals(user cshAuth.CSHUserInfo) bool { - return DEV_FORCE_IS_EVALS || containsString(user.Groups, "eboard-evaluations") + return DEV_FORCE_IS_EVALS || slices.Contains(user.Groups, "eboard-evaluations") +} + +func isChair(user cshAuth.CSHUserInfo) bool { + return DEV_FORCE_IS_CHAIR || slices.Contains(user.Groups, "eboard-chairman") } // canVote determines whether a user can cast a vote. @@ -618,12 +617,3 @@ func hasOption(poll *database.Poll, option string) bool { } return false } - -func containsString(arr []string, val string) bool { - for _, a := range arr { - if a == val { - return true - } - } - return false -} diff --git a/templates/create.tmpl b/templates/create.tmpl index fbfecef..19302ef 100644 --- a/templates/create.tmpl +++ b/templates/create.tmpl @@ -5,13 +5,13 @@ - + {{ template "nav" . }} - +

Create Poll

@@ -59,11 +59,26 @@
Ranked Choice Vote + + E-Board Vote + + Simple (Single-Choice) Vote
Number of Eligible Voters: {{ len .EligibleVoters }}
{{/* This works currently because quorum type can only be set if gatekeep is required */}} - {{ if not .Gatekeep }} + {{ if not (len .EligibleVoters) }}
No quorum required for this poll.
{{ else }}
Quorum Type: {{ .Quorum }}%
diff --git a/users.go b/users.go index 12abfbf..1f1d741 100644 --- a/users.go +++ b/users.go @@ -110,6 +110,30 @@ func (client *OIDCClient) GetActiveUsers() []OIDCUser { return ret } +func (client *OIDCClient) GetEBoard() []OIDCUser { + htclient := &http.Client{} + //active + req, err := http.NewRequest("GET", client.providerBase+"/auth/admin/realms/csh/groups/47dd1a94-853c-426d-b181-6d0714074892/members", nil) + if err != nil { + logging.Logger.WithFields(logrus.Fields{"method": "GetEBoard"}).Error(err) + return nil + } + req.Header.Add("Authorization", "Bearer "+client.accessToken) + resp, err := htclient.Do(req) + if err != nil { + logging.Logger.WithFields(logrus.Fields{"method": "GetEBoard"}).Error(err) + return nil + } + defer resp.Body.Close() + ret := make([]OIDCUser, 0) + err = json.NewDecoder(resp.Body).Decode(&ret) + if err != nil { + logging.Logger.WithFields(logrus.Fields{"method": "GetEBoard"}).Error(err) + return nil + } + return ret +} + func (client *OIDCClient) GetUserInfo(user *OIDCUser) { htclient := &http.Client{} arg := "" From 1907969920c34c94fdeb71360b66b6ba27b6b2fa Mon Sep 17 00:00:00 2001 From: Tyler Allen Date: Thu, 5 Mar 2026 13:57:05 -0500 Subject: [PATCH 2/6] use org-wide pr template --- .github/pull_request_template.md | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 1cd3021..0000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,19 +0,0 @@ -## What - -_what the PR changes_ - -## Why - -_why these changes were made_ - -## Test Plan - -_how did you verify these changes did what you expected_ - -## Env Vars - -_did you add, remove, or rename any environment variables_ - -## Checklist - -- [ ] Tested all changes locally From 57ab24be3bb05a8d3130f1ef740265c958a53b57 Mon Sep 17 00:00:00 2001 From: Tyler Allen Date: Thu, 5 Mar 2026 14:04:40 -0500 Subject: [PATCH 3/6] reduce cognitive complexity --- main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.go b/main.go index 363884c..ecfe957 100644 --- a/main.go +++ b/main.go @@ -520,8 +520,7 @@ func main() { } if poll.CreatedBy != claims.UserInfo.Username { - if slices.Contains(claims.UserInfo.Groups, "active_rtp") || slices.Contains(claims.UserInfo.Groups, "eboard") { - } else { + if !(slices.Contains(claims.UserInfo.Groups, "active_rtp") || slices.Contains(claims.UserInfo.Groups, "eboard")) { c.JSON(http.StatusForbidden, gin.H{"error": "You cannot end this poll."}) return } From 22517b8b310c77c3ebd0e6476930f75ea27baf3b Mon Sep 17 00:00:00 2001 From: Tyler Allen Date: Thu, 5 Mar 2026 14:08:25 -0500 Subject: [PATCH 4/6] reduce duplicated code --- users.go | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/users.go b/users.go index 1f1d741..26c4ee4 100644 --- a/users.go +++ b/users.go @@ -87,48 +87,32 @@ func (client *OIDCClient) getAccessToken() int { } func (client *OIDCClient) GetActiveUsers() []OIDCUser { - htclient := &http.Client{} - //active - req, err := http.NewRequest("GET", client.providerBase+"/auth/admin/realms/csh/groups/a97a191e-5668-43f5-bc0c-6eefc2b958a7/members", nil) - if err != nil { - logging.Logger.WithFields(logrus.Fields{"method": "GetActiveUsers"}).Error(err) - return nil - } - req.Header.Add("Authorization", "Bearer "+client.accessToken) - resp, err := htclient.Do(req) - if err != nil { - logging.Logger.WithFields(logrus.Fields{"method": "GetActiveUsers"}).Error(err) - return nil - } - defer resp.Body.Close() - ret := make([]OIDCUser, 0) - err = json.NewDecoder(resp.Body).Decode(&ret) - if err != nil { - logging.Logger.WithFields(logrus.Fields{"method": "GetActiveUsers"}).Error(err) - return nil - } - return ret + return client.GetOIDCGroup("a97a191e-5668-43f5-bc0c-6eefc2b958a7") } func (client *OIDCClient) GetEBoard() []OIDCUser { + return client.GetOIDCGroup("47dd1a94-853c-426d-b181-6d0714074892") +} + +func (client *OIDCClient) GetOIDCGroup(groupID string) []OIDCUser { htclient := &http.Client{} //active - req, err := http.NewRequest("GET", client.providerBase+"/auth/admin/realms/csh/groups/47dd1a94-853c-426d-b181-6d0714074892/members", nil) + req, err := http.NewRequest("GET", client.providerBase+"/auth/admin/realms/csh/groups/"+groupID+"/members", nil) if err != nil { - logging.Logger.WithFields(logrus.Fields{"method": "GetEBoard"}).Error(err) + logging.Logger.WithFields(logrus.Fields{"method": "GetOIDCGroup"}).Error(err) return nil } req.Header.Add("Authorization", "Bearer "+client.accessToken) resp, err := htclient.Do(req) if err != nil { - logging.Logger.WithFields(logrus.Fields{"method": "GetEBoard"}).Error(err) + logging.Logger.WithFields(logrus.Fields{"method": "GetOIDCGroup"}).Error(err) return nil } defer resp.Body.Close() ret := make([]OIDCUser, 0) err = json.NewDecoder(resp.Body).Decode(&ret) if err != nil { - logging.Logger.WithFields(logrus.Fields{"method": "GetEBoard"}).Error(err) + logging.Logger.WithFields(logrus.Fields{"method": "GetOIDCGroup"}).Error(err) return nil } return ret From c14efc271278dd693f5dbe256d32e964b4a13791 Mon Sep 17 00:00:00 2001 From: Tyler Allen Date: Thu, 5 Mar 2026 14:17:15 -0500 Subject: [PATCH 5/6] fix spacing --- templates/create.tmpl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/templates/create.tmpl b/templates/create.tmpl index 19302ef..139b71d 100644 --- a/templates/create.tmpl +++ b/templates/create.tmpl @@ -65,18 +65,18 @@ > Ranked Choice Vote E-Board Vote Simple (Single-Choice) Vote
From 6462009258f0519f5e6e055d28742db7daa179f9 Mon Sep 17 00:00:00 2001 From: Tyler Allen Date: Thu, 5 Mar 2026 14:23:48 -0500 Subject: [PATCH 6/6] more fix spacing --- templates/create.tmpl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/templates/create.tmpl b/templates/create.tmpl index 139b71d..b30c946 100644 --- a/templates/create.tmpl +++ b/templates/create.tmpl @@ -64,21 +64,21 @@ value="rankedChoice" > Ranked Choice Vote - - E-Board Vote - - Simple (Single-Choice) Vote + + E-Board Vote + + Simple (Single-Choice) Vote