Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions .github/pull_request_template.md

This file was deleted.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
63 changes: 26 additions & 37 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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") {
Expand All @@ -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")
}
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -525,8 +520,7 @@ func main() {
}

if poll.CreatedBy != claims.UserInfo.Username {
if containsString(claims.UserInfo.Groups, "active_rtp") || containsString(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
}
Expand Down Expand Up @@ -563,7 +557,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.
Expand Down Expand Up @@ -618,12 +616,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
}
25 changes: 20 additions & 5 deletions templates/create.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<!-- <link rel="stylesheet" href="https://themeswitcher.csh.rit.edu/api/get" /> -->
<link rel="stylesheet" href="https://assets.csh.rit.edu/csh-material-bootstrap/4.3.1/dist/csh-material-bootstrap.min.css" media="screen">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css">

<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
{{ template "nav" . }}

<div class="container main p-5">
<h2>Create Poll</h2>
<form action="/create" method="POST">
Expand Down Expand Up @@ -59,11 +59,26 @@
</div>
<div class="form-group">
<input
type="checkbox"
name="rankedChoice"
value="true"
type="radio"
name="pollType"
value="rankedChoice"
>
<span>Ranked Choice Vote</span>
<input
type="radio"
name="pollType"
value="eboard"
class="ml-3"
>
<span>E-Board Vote</span>
<input
type="radio"
name="pollType"
value="simple"
class="ml-3"
checked="checked"
>
<span>Simple (Single-Choice) Vote</span>
</div>
<div class="form-group">
<input
Expand Down
2 changes: 1 addition & 1 deletion templates/result.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<h5>Number of Eligible Voters: {{ len .EligibleVoters }}</h5>
<div id="votes-needed-for-quorum">
{{/* This works currently because quorum type can only be set if gatekeep is required */}}
{{ if not .Gatekeep }}
{{ if not (len .EligibleVoters) }}
<h5>No quorum required for this poll.</h5>
{{ else }}
<h5>Quorum Type: {{ .Quorum }}%</h5>
Expand Down
16 changes: 12 additions & 4 deletions users.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,32 @@ func (client *OIDCClient) getAccessToken() int {
}

func (client *OIDCClient) GetActiveUsers() []OIDCUser {
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/a97a191e-5668-43f5-bc0c-6eefc2b958a7/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": "GetActiveUsers"}).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": "GetActiveUsers"}).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": "GetActiveUsers"}).Error(err)
logging.Logger.WithFields(logrus.Fields{"method": "GetOIDCGroup"}).Error(err)
return nil
}
return ret
Expand Down