Added configurable HTTP Basic Auth.

This commit is contained in:
Patrick Fairbank
2015-08-22 23:33:38 -07:00
parent bd098de716
commit 5e49142ef0
23 changed files with 305 additions and 4 deletions

View File

@@ -16,6 +16,10 @@ import (
// Renders the team number and status display shown above each alliance station.
func AllianceStationDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
template := template.New("").Funcs(templateHelpers)
_, err := template.ParseFiles("templates/alliance_station_display.html")
if err != nil {
@@ -42,6 +46,10 @@ func AllianceStationDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the alliance station display client to receive status updates.
func AllianceStationDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
websocket, err := NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)

View File

@@ -15,6 +15,10 @@ import (
// Renders the announcer display which shows team info and scores for the current match.
func AnnouncerDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
template := template.New("").Funcs(templateHelpers)
_, err := template.ParseFiles("templates/announcer_display.html", "templates/base.html")
if err != nil {
@@ -33,6 +37,10 @@ func AnnouncerDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the announcer display client to send control commands and receive status updates.
func AnnouncerDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
websocket, err := NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)

12
api.go
View File

@@ -23,6 +23,10 @@ type RankingWithNickname struct {
// Generates a JSON dump of the matches and results.
func MatchesApiHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
vars := mux.Vars(r)
matches, err := db.GetMatchesByType(vars["type"])
if err != nil {
@@ -57,6 +61,10 @@ func MatchesApiHandler(w http.ResponseWriter, r *http.Request) {
// Generates a JSON dump of the sponsor slides for use by the audience display.
func SponsorSlidesApiHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
sponsors, err := db.GetAllSponsorSlides()
if err != nil {
handleWebErr(w, err)
@@ -79,6 +87,10 @@ func SponsorSlidesApiHandler(w http.ResponseWriter, r *http.Request) {
// Generates a JSON dump of the qualification rankings, primarily for use by the pit display.
func RankingsApiHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
rankings, err := db.GetAllRankings()
if err != nil {
handleWebErr(w, err)

View File

@@ -14,6 +14,10 @@ import (
// Renders the audience display to be chroma keyed over the video feed.
func AudienceDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
template := template.New("").Funcs(templateHelpers)
_, err := template.ParseFiles("templates/audience_display.html")
if err != nil {
@@ -33,6 +37,10 @@ func AudienceDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the audience display client to receive status updates.
func AudienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
websocket, err := NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)

View File

@@ -20,7 +20,11 @@ CREATE TABLE event_settings (
appassword VARCHAR(255),
switchaddress VARCHAR(255),
switchpassword VARCHAR(255),
bandwidthmonitoringenabled bool
bandwidthmonitoringenabled bool,
tbadownloadenabled bool,
tbaawardsdownloadenabled bool,
adminpassword VARCHAR(255),
readerpassword VARCHAR(255)
);
-- +goose Down

View File

@@ -1,3 +0,0 @@
-- +goose Up
ALTER TABLE event_settings ADD COLUMN tbadownloadenabled BOOLEAN;
ALTER TABLE event_settings ADD COLUMN tbaawardsdownloadenabled BOOLEAN;

View File

@@ -28,6 +28,8 @@ type EventSettings struct {
SwitchAddress string
SwitchPassword string
BandwidthMonitoringEnabled bool
AdminPassword string
ReaderPassword string
}
const eventSettingsId = 0

View File

@@ -12,6 +12,10 @@ import (
// Renders the FTA diagnostic display.
func FtaDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
template := template.New("").Funcs(templateHelpers)
_, err := template.ParseFiles("templates/fta_display.html", "templates/base.html")
if err != nil {

View File

@@ -38,6 +38,10 @@ var currentMatchType string
// Shows the match play control interface.
func MatchPlayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
practiceMatches, err := buildMatchPlayList("practice")
if err != nil {
handleWebErr(w, err)
@@ -89,6 +93,10 @@ func MatchPlayHandler(w http.ResponseWriter, r *http.Request) {
// Loads the given match onto the arena in preparation for playing it.
func MatchPlayLoadHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
vars := mux.Vars(r)
matchId, _ := strconv.Atoi(vars["matchId"])
var match *Match
@@ -118,6 +126,10 @@ func MatchPlayLoadHandler(w http.ResponseWriter, r *http.Request) {
// Loads the results for the given match into the display buffer.
func MatchPlayShowResultHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
vars := mux.Vars(r)
matchId, _ := strconv.Atoi(vars["matchId"])
match, err := db.GetMatchById(matchId)
@@ -147,6 +159,10 @@ func MatchPlayShowResultHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the match play client to send control commands and receive status updates.
func MatchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
websocket, err := NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)

View File

@@ -26,6 +26,10 @@ type MatchReviewListItem struct {
// Shows the match review interface.
func MatchReviewHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
practiceMatches, err := buildMatchReviewList("practice")
if err != nil {
handleWebErr(w, err)
@@ -66,6 +70,10 @@ func MatchReviewHandler(w http.ResponseWriter, r *http.Request) {
// Shows the page to edit the results for a match.
func MatchReviewEditGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
match, matchResult, _, err := getMatchResultFromRequest(r)
if err != nil {
handleWebErr(w, err)
@@ -96,6 +104,10 @@ func MatchReviewEditGetHandler(w http.ResponseWriter, r *http.Request) {
// Updates the results for a match.
func MatchReviewEditPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
match, matchResult, isCurrent, err := getMatchResultFromRequest(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -14,6 +14,10 @@ import (
// Renders the pit display which shows scrolling rankings.
func PitDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
template, err := template.ParseFiles("templates/pit_display.html")
if err != nil {
handleWebErr(w, err)
@@ -31,6 +35,10 @@ func PitDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the pit display, used only to force reloads remotely.
func PitDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
websocket, err := NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)

View File

@@ -20,6 +20,10 @@ var rules = []string{"G4", "G5", "G6", "G6-1", "G16", "G17", "G18", "G19", "G20"
// Renders the referee interface for assigning fouls.
func RefereeDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
template := template.New("").Funcs(templateHelpers)
_, err := template.ParseFiles("templates/referee_display.html")
if err != nil {
@@ -82,6 +86,10 @@ func RefereeDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the refereee interface client to send control commands and receive status updates.
func RefereeDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
websocket, err := NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)

View File

@@ -16,6 +16,10 @@ import (
// Generates a CSV-formatted report of the qualification rankings.
func RankingsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
rankings, err := db.GetAllRankings()
if err != nil {
handleWebErr(w, err)
@@ -38,6 +42,10 @@ func RankingsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
// Generates a PDF-formatted report of the qualification rankings.
func RankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
rankings, err := db.GetAllRankings()
if err != nil {
handleWebErr(w, err)
@@ -93,6 +101,10 @@ func RankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
// Generates a CSV-formatted report of the match schedule.
func ScheduleCsvReportHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
vars := mux.Vars(r)
matches, err := db.GetMatchesByType(vars["type"])
if err != nil {
@@ -116,6 +128,10 @@ func ScheduleCsvReportHandler(w http.ResponseWriter, r *http.Request) {
// Generates a PDF-formatted report of the match schedule.
func SchedulePdfReportHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
vars := mux.Vars(r)
matches, err := db.GetMatchesByType(vars["type"])
if err != nil {
@@ -221,6 +237,10 @@ func SchedulePdfReportHandler(w http.ResponseWriter, r *http.Request) {
// Generates a CSV-formatted report of the team list.
func TeamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
teams, err := db.GetAllTeams()
if err != nil {
handleWebErr(w, err)
@@ -243,6 +263,10 @@ func TeamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
// Generates a PDF-formatted report of the team list.
func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsReader(w, r) {
return
}
teams, err := db.GetAllTeams()
if err != nil {
handleWebErr(w, err)
@@ -285,6 +309,10 @@ func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
// Generates a CSV-formatted report of the WPA keys, for import into the radio kiosk.
func WpaKeysCsvReportHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
teams, err := db.GetAllTeams()
if err != nil {
handleWebErr(w, err)

View File

@@ -17,6 +17,10 @@ import (
// Renders the scoring interface which enables input of scores in real-time.
func ScoringDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
vars := mux.Vars(r)
alliance := vars["alliance"]
if alliance != "red" && alliance != "blue" {
@@ -42,6 +46,10 @@ func ScoringDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the scoring interface client to send control commands and receive status updates.
func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
vars := mux.Vars(r)
alliance := vars["alliance"]
if alliance != "red" && alliance != "blue" {

View File

@@ -25,11 +25,19 @@ var cachedRankedTeams []*RankedTeam
// Shows the alliance selection page.
func AllianceSelectionGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
renderAllianceSelection(w, r, "")
}
// Updates the cache with the latest input from the client.
func AllianceSelectionPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if !canModifyAllianceSelection() {
renderAllianceSelection(w, r, "Alliance selection has already been finalized.")
return
@@ -81,6 +89,10 @@ func AllianceSelectionPostHandler(w http.ResponseWriter, r *http.Request) {
// Sets up the empty alliances and populates the ranked team list.
func AllianceSelectionStartHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if len(cachedAlliances) != 0 {
renderAllianceSelection(w, r, "Can't start alliance selection when it is already in progress.")
return
@@ -120,6 +132,10 @@ func AllianceSelectionStartHandler(w http.ResponseWriter, r *http.Request) {
// Resets the alliance selection process back to the starting point.
func AllianceSelectionResetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if !canModifyAllianceSelection() {
renderAllianceSelection(w, r, "Alliance selection has already been finalized.")
return
@@ -133,6 +149,10 @@ func AllianceSelectionResetHandler(w http.ResponseWriter, r *http.Request) {
// Saves the selected alliances to the database and generates the first round of elimination matches.
func AllianceSelectionFinalizeHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if !canModifyAllianceSelection() {
renderAllianceSelection(w, r, "Alliance selection has already been finalized.")
return

View File

@@ -12,6 +12,10 @@ import (
// Shows the field configuration page.
func FieldGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
template, err := template.ParseFiles("templates/setup_field.html", "templates/base.html")
if err != nil {
handleWebErr(w, err)
@@ -31,6 +35,10 @@ func FieldGetHandler(w http.ResponseWriter, r *http.Request) {
// Updates the display-station mapping for a single display.
func FieldPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
displayId := r.PostFormValue("displayId")
allianceStation := r.PostFormValue("allianceStation")
mainArena.allianceStationDisplays[displayId] = allianceStation
@@ -40,12 +48,20 @@ func FieldPostHandler(w http.ResponseWriter, r *http.Request) {
// Force-reloads all the websocket-connected displays.
func FieldReloadDisplaysHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
mainArena.reloadDisplaysNotifier.Notify(nil)
http.Redirect(w, r, "/setup/field", 302)
}
// Controls the field LEDs for testing or effect.
func FieldLightsPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
mainArena.lights.SetMode(r.PostFormValue("mode"))
http.Redirect(w, r, "/setup/field", 302)
}

View File

@@ -16,6 +16,10 @@ import (
// Shows the lower third configuration page.
func LowerThirdsGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
template, err := template.ParseFiles("templates/setup_lower_thirds.html", "templates/base.html")
if err != nil {
handleWebErr(w, err)
@@ -39,6 +43,10 @@ func LowerThirdsGetHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the lower thirds client to send control commands.
func LowerThirdsWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
websocket, err := NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)

View File

@@ -21,6 +21,10 @@ var cachedTeamFirstMatches map[int]string
// Shows the schedule editing page.
func ScheduleGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if len(cachedScheduleBlocks) == 0 {
tomorrow := time.Now().AddDate(0, 0, 1)
location, _ := time.LoadLocation("Local")
@@ -33,6 +37,10 @@ func ScheduleGetHandler(w http.ResponseWriter, r *http.Request) {
// Generates the schedule and presents it for review without saving it to the database.
func ScheduleGeneratePostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
r.ParseForm()
cachedMatchType = r.PostFormValue("matchType")
scheduleBlocks, err := getScheduleBlocks(r)
@@ -88,6 +96,10 @@ func ScheduleGeneratePostHandler(w http.ResponseWriter, r *http.Request) {
// Saves the generated schedule to the database.
func ScheduleSavePostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
existingMatches, err := db.GetMatchesByType(cachedMatchType)
if err != nil {
handleWebErr(w, err)

View File

@@ -20,11 +20,19 @@ import (
// Shows the event settings editing page.
func SettingsGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
renderSettings(w, r, "")
}
// Saves the event settings.
func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
eventSettings.Name = r.PostFormValue("name")
eventSettings.Code = r.PostFormValue("code")
match, _ := regexp.MatchString("^#([0-9A-Fa-f]{3}){1,2}$", r.PostFormValue("displayBackgroundColor"))
@@ -57,6 +65,8 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
eventSettings.SwitchAddress = r.PostFormValue("switchAddress")
eventSettings.SwitchPassword = r.PostFormValue("switchPassword")
eventSettings.BandwidthMonitoringEnabled = r.PostFormValue("bandwidthMonitoringEnabled") == "on"
eventSettings.AdminPassword = r.PostFormValue("adminPassword")
eventSettings.ReaderPassword = r.PostFormValue("readerPassword")
err := db.SaveEventSettings(eventSettings)
if err != nil {
handleWebErr(w, err)
@@ -75,6 +85,10 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
// Sends a copy of the event database file to the client as a download.
func SaveDbHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
dbFile, err := os.Open(db.path)
defer dbFile.Close()
if err != nil {
@@ -89,6 +103,10 @@ func SaveDbHandler(w http.ResponseWriter, r *http.Request) {
// Accepts an event database file as an upload and loads it.
func RestoreDbHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
file, _, err := r.FormFile("databaseFile")
if err != nil {
renderSettings(w, r, "No database backup file was specified.")
@@ -144,6 +162,10 @@ func RestoreDbHandler(w http.ResponseWriter, r *http.Request) {
// Deletes all data except for the team list.
func ClearDbHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
// Back up the database.
err := db.Backup("pre_clear")
if err != nil {

View File

@@ -13,6 +13,10 @@ import (
// Shows the sponsor slides configuration page.
func SponsorSlidesGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
template, err := template.ParseFiles("templates/setup_sponsor_slides.html", "templates/base.html")
if err != nil {
handleWebErr(w, err)
@@ -36,6 +40,10 @@ func SponsorSlidesGetHandler(w http.ResponseWriter, r *http.Request) {
// Saves the new or modified sponsor slides to the database.
func SponsorSlidesPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
sponsorSlideId, _ := strconv.Atoi(r.PostFormValue("id"))
sponsorSlide, err := db.GetSponsorSlideById(sponsorSlideId)
if err != nil {

View File

@@ -21,11 +21,19 @@ const wpaKeyLength = 8
// Shows the team list.
func TeamsGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
renderTeams(w, r, false)
}
// Adds teams to the team list.
func TeamsPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if !canModifyTeamList() {
renderTeams(w, r, true)
return
@@ -56,6 +64,10 @@ func TeamsPostHandler(w http.ResponseWriter, r *http.Request) {
// Clears the team list.
func TeamsClearHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if !canModifyTeamList() {
renderTeams(w, r, true)
return
@@ -71,6 +83,10 @@ func TeamsClearHandler(w http.ResponseWriter, r *http.Request) {
// Shows the page to edit a team's fields.
func TeamEditGetHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
vars := mux.Vars(r)
teamId, _ := strconv.Atoi(vars["id"])
team, err := db.GetTeamById(teamId)
@@ -101,6 +117,10 @@ func TeamEditGetHandler(w http.ResponseWriter, r *http.Request) {
// Updates a team's fields.
func TeamEditPostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
vars := mux.Vars(r)
teamId, _ := strconv.Atoi(vars["id"])
team, err := db.GetTeamById(teamId)
@@ -138,6 +158,10 @@ func TeamEditPostHandler(w http.ResponseWriter, r *http.Request) {
// Removes a team from the team list.
func TeamDeletePostHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
if !canModifyTeamList() {
renderTeams(w, r, true)
return
@@ -164,6 +188,10 @@ func TeamDeletePostHandler(w http.ResponseWriter, r *http.Request) {
// Publishes the team list to the web.
func TeamsPublishHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
err := PublishTeams()
if err != nil {
http.Error(w, "Failed to publish teams: "+err.Error(), 500)
@@ -174,6 +202,10 @@ func TeamsPublishHandler(w http.ResponseWriter, r *http.Request) {
// Generates random WPA keys and saves them to the team models.
func TeamsGenerateWpaKeysHandler(w http.ResponseWriter, r *http.Request) {
if !UserIsAdmin(w, r) {
return
}
generateAllKeys := false
if all, ok := r.URL.Query()["all"]; ok {
generateAllKeys = all[0] == "true"

View File

@@ -135,6 +135,22 @@
</div>
</div>
</fieldset>
<fieldset>
<legend>Authentication</legend>
<p>Configure passwords to enable HTTP Basic authentication, or leave blank to disable.</p>
<div class="form-group">
<label class="col-lg-5 control-label">Password for 'admin' user</label>
<div class="col-lg-7">
<input type="password" class="form-control" name="adminPassword" value="{{.AdminPassword}}">
</div>
</div>
<div class="form-group">
<label class="col-lg-5 control-label">Password for 'reader' user</label>
<div class="col-lg-7">
<input type="password" class="form-control" name="readerPassword" value="{{.ReaderPassword}}">
</div>
</div>
</fieldset>
<fieldset>
<legend>Field I/O</legend>
<div class="form-group">

44
web.go
View File

@@ -6,6 +6,7 @@
package main
import (
"bitbucket.org/rj/httpauth-go"
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
@@ -15,8 +16,12 @@ import (
)
const httpPort = 8080
const adminUser = "admin"
const readerUser = "reader"
var websocketUpgrader = websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 2014}
var adminAuth = httpauth.NewBasic("Cheesy Arena", checkAdminPassword, nil)
var readerAuth = httpauth.NewBasic("Cheesy Arena", checkReaderPassword, nil)
// Helper functions that can be used inside templates.
var templateHelpers = template.FuncMap{
@@ -105,6 +110,45 @@ func ServeWebInterface() {
http.ListenAndServe(fmt.Sprintf(":%d", httpPort), nil)
}
// Returns true if the given user is authorized for admin operations. Used for HTTP Basic Auth.
func UserIsAdmin(w http.ResponseWriter, r *http.Request) bool {
if eventSettings.AdminPassword == "" {
// Disable auth if there is no password configured.
return true
}
if adminAuth.Authorize(r) == "" {
adminAuth.NotifyAuthRequired(w, r)
return false
}
return true
}
// Returns true if the given user is authorized for read-only operations. Used for HTTP Basic Auth.
func UserIsReader(w http.ResponseWriter, r *http.Request) bool {
if eventSettings.ReaderPassword == "" {
// Disable auth if there is no password configured.
return true
}
if readerAuth.Authorize(r) == "" {
readerAuth.NotifyAuthRequired(w, r)
return false
}
return true
}
func checkAdminPassword(user, password string) bool {
return user == adminUser && password == eventSettings.AdminPassword
}
func checkReaderPassword(user, password string) bool {
if user == readerUser {
return password == eventSettings.ReaderPassword
}
// The admin role also has read permissions.
return checkAdminPassword(user, password)
}
// Sets up the mapping between URLs and handlers.
func newHandler() http.Handler {
router := mux.NewRouter()