You're not authorized to vote!
+You're not authorized to vote or view this poll!
It looks like you're either not marked as active, or you're on co-op right now
diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3026b2e --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +VOTE_HOST=http://localhost:8080 +VOTE_STATE=1000000 +VOTE_OIDC_ID=develop +VOTE_JWT_SECRET=ahahaisdfjioThisShouldBeRandom +VOTE_OIDC_SECRET=no peeking (on wiki) +DEV_DISABLE_ACTIVE_FILTERS=true +DEV_FORCE_IS_EVALS=true +VOTE_CONDITIONAL_URL=https://conditional.csh.rit.edu/gatekeep +VOTE_TOKEN=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +VOTE_MONGODB_URI=mongodb://vote:c1f66aac6b4fafbef3c659371b8a50ed@mongodb/vote?authSource=admin +VOTE_ANNOUNCEMENTS_CHANNEL_ID= +VOTE_SLACK_APP_TOKEN= +VOTE_SLACK_BOT_TOKEN= diff --git a/.gitignore b/.gitignore index 20e2001..212e1b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -.env* +.env vote .idea/ -.vscode/ \ No newline at end of file +.vscode/ + diff --git a/README.md b/README.md index 306981a..a692756 100644 --- a/README.md +++ b/README.md @@ -11,23 +11,7 @@ Implementation ## Configuration -If you're using the compose file, you'll need to ask an RTP for the vote-dev OIDC secret, and set it as `VOTE_OIDC_SECRET` in your environment - -If you're not using the compose file, you'll need more of these - -``` -VOTE_HOST=http://localhost:8080 -VOTE_JWT_SECRET= -VOTE_MONGODB_URI= -VOTE_OIDC_ID= -VOTE_OIDC_SECRET= -VOTE_STATE= -VOTE_TOKEN= -VOTE_CONDITIONAL_URL=https://conditional.csh.rit.edu/gatekeep/ -VOTE_ANNOUNCEMENTS_CHANNEL_ID= -VOTE_SLACK_APP_TOKEN= -VOTE_SLACK_BOT_TOKEN= -``` +Copy `.env.example` to `.env` and fill out the info ### Dev Overrides `DEV_DISABLE_ACTIVE_FILTERS="true"` will disable the requirements that you be active to vote diff --git a/api.go b/api.go index 6bf8567..8fe6c7c 100644 --- a/api.go +++ b/api.go @@ -38,15 +38,29 @@ func GetHomepage(c *gin.Context) { // A user may be unable to vote but should still be able to see a list of polls user := GetUserData(c) - polls, err := database.GetOpenPolls(c) + pollResults, err := database.GetOpenPolls(c) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - sort.Slice(polls, func(i, j int) bool { - return polls[i].Id > polls[j].Id + sort.Slice(pollResults, func(i, j int) bool { + return pollResults[i].Id > pollResults[j].Id }) + polls := []*database.Poll{} + + for _, poll := range pollResults { + if poll.HideForIneligible { + canVoteResult := canVote(user, *poll, poll.AllowedUsers) + + if canVoteResult != 0 && canVoteResult != 9 { + continue + } + } + + polls = append(polls, poll) + } + c.HTML(http.StatusOK, "index.tmpl", gin.H{ "Polls": polls, "Username": user.Username, @@ -139,23 +153,30 @@ func CreatePoll(c *gin.Context) { return } + // If title length exceeds 250 characters, return a bad request + if len(c.PostForm("title")) > 250 { + c.JSON(http.StatusBadRequest, gin.H{"error": "title length exceeds limit of 250 characters"}) + return + } + quorumType := c.PostForm("quorumType") quorum, err := strconv.ParseFloat(quorumType, 64) quorum = quorum / 100 poll := &database.Poll{ - Id: "", - CreatedBy: user.Username, - Title: c.PostForm("title"), - Description: c.PostForm("description"), - VoteType: database.POLL_TYPE_SIMPLE, - OpenedTime: time.Now(), - Open: true, - QuorumType: quorum, - Gatekeep: c.PostForm("gatekeep") == "true", - AllowWriteIns: c.PostForm("allowWriteIn") == "true", - Hidden: c.PostForm("hidden") == "true", + Id: "", + CreatedBy: user.Username, + Title: c.PostForm("title"), + Description: c.PostForm("description"), + VoteType: database.POLL_TYPE_SIMPLE, + OpenedTime: time.Now(), + Open: true, + QuorumType: quorum, + Gatekeep: c.PostForm("gatekeep") == "true", + AllowWriteIns: c.PostForm("allowWriteIn") == "true", + Hidden: c.PostForm("hidden") == "true", + HideForIneligible: c.PostForm("hideIneligible") == "true", } if c.PostForm("rankedChoice") == "true" { poll.VoteType = database.POLL_TYPE_RANKED @@ -189,6 +210,10 @@ func CreatePoll(c *gin.Context) { } poll.AllowedUsers = GetEligibleVoters() for user := range strings.SplitSeq(c.PostForm("waivedUsers"), ",") { + if len(user) == 0 { // When it's empty (and probably in other cases) the split can return an empty string, which changes the total + continue + } + poll.AllowedUsers = append(poll.AllowedUsers, strings.TrimSpace(user)) } } @@ -243,6 +268,19 @@ func GetPollResults(c *gin.Context) { canModify := IsActiveRTP(user) || IsEboard(user) || ownsPoll(poll, user) + if poll.HideForIneligible { + canVoteResult := canVote(user, *poll, poll.AllowedUsers) + + if canVoteResult != 0 && canVoteResult != 9 { + c.HTML(http.StatusForbidden, "unauthorized.tmpl", gin.H{ + "Username": user.Username, + "FullName": user.FullName, + "EBoard": IsEboard(user), + }) + return + } + } + if poll.Hidden && poll.Open { c.HTML(http.StatusUnauthorized, "hidden.tmpl", gin.H{ "Id": poll.Id, @@ -256,10 +294,8 @@ func GetPollResults(c *gin.Context) { numVotes := 0 - for _, v := range results { - for key := range v { - numVotes += v[key] - } + for key := range results[0] { + numVotes += results[0][key] } c.HTML(http.StatusOK, "result.tmpl", gin.H{ diff --git a/database/poll.go b/database/poll.go index 7df45fc..77b9f2b 100644 --- a/database/poll.go +++ b/database/poll.go @@ -16,18 +16,19 @@ import ( ) type Poll struct { - Id string `bson:"_id,omitempty"` - CreatedBy string `bson:"createdBy"` - Title string `bson:"title"` - Description string `bson:"description"` - VoteType string `bson:"voteType"` - Options []string `bson:"options"` - OpenedTime time.Time `bson:"openedTime"` - Open bool `bson:"open"` - Gatekeep bool `bson:"gatekeep"` - QuorumType float64 `bson:"quorumType"` - AllowedUsers []string `bson:"allowedUsers"` - AllowWriteIns bool `bson:"writeins"` + Id string `bson:"_id,omitempty"` + CreatedBy string `bson:"createdBy"` + Title string `bson:"title"` + Description string `bson:"description"` + VoteType string `bson:"voteType"` + Options []string `bson:"options"` + OpenedTime time.Time `bson:"openedTime"` + Open bool `bson:"open"` + Gatekeep bool `bson:"gatekeep"` + HideForIneligible bool `bson:"hideIneligible"` + QuorumType float64 `bson:"quorumType"` + AllowedUsers []string `bson:"allowedUsers"` + AllowWriteIns bool `bson:"writeins"` // Prevent this poll from having progress displayed // This is important for events like elections where the results shouldn't be visible mid vote diff --git a/docker-compose.yaml b/docker-compose.yaml index 4902a92..0d279f3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -13,15 +13,9 @@ services: container_name: vote depends_on: - mongodb - environment: - 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_SECRET: "${VOTE_OIDC_SECRET}" - VOTE_STATE: 27a28540e47ec786b7bdad03f83171b3 - DEV_DISABLE_ACTIVE_FILTERS: "${DEV_DISABLE_ACTIVE_FILTERS}" - DEV_FORCE_IS_EVALS: "${DEV_FORCE_IS_EVALS}" + env_file: + - path: .env + required: false ports: - "127.0.0.1:8080:8080" diff --git a/templates/create.tmpl b/templates/create.tmpl index 5739f7e..84e823e 100644 --- a/templates/create.tmpl +++ b/templates/create.tmpl @@ -9,7 +9,8 @@ type="text" class="form-control" name="title" - placeholder="My Poll" + placeholder="My Poll (max 250 characters)" + maxlength="250" required > @@ -100,7 +101,7 @@ value="50" > -
This is a Ranked Choice vote. Rank the candidates in order of your preference. 1 is most preferred, and {{ .RankedMax }} is least perferred. You may leave an option blank +
This is a Ranked Choice vote. Rank the candidates in order of your preference. 1 is most preferred, and {{ .RankedMax }} is least preferred. You may leave an option blank if you do not prefer it at all.
{{ end }} diff --git a/templates/unauthorized.tmpl b/templates/unauthorized.tmpl index b84ade8..708ac2e 100644 --- a/templates/unauthorized.tmpl +++ b/templates/unauthorized.tmpl @@ -2,7 +2,7 @@It looks like you're either not marked as active, or you're on co-op right now