Remove 'reader' authenticated user as it just complicates display and API access.

This commit is contained in:
Patrick Fairbank
2019-08-19 19:12:16 -07:00
parent 6a7dd76445
commit 9569c04912
17 changed files with 11 additions and 170 deletions

View File

@@ -22,7 +22,6 @@ CREATE TABLE event_settings (
plcaddress VARCHAR(255),
tbadownloadenabled bool,
adminpassword VARCHAR(255),
readerpassword VARCHAR(255),
habdockingthreshold int
);

View File

@@ -27,7 +27,6 @@ type EventSettings struct {
SwitchPassword string
PlcAddress string
AdminPassword string
ReaderPassword string
HabDockingThreshold int
}

View File

@@ -115,19 +115,13 @@
</fieldset>
<fieldset>
<legend>Authentication</legend>
<p>Configure passwords to enable HTTP Basic authentication, or leave blank to disable.</p>
<p>Configure password to enable 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>Networking</legend>

View File

@@ -13,10 +13,6 @@ import (
// Renders the team number and status display shown above each alliance station.
func (web *Web) allianceStationDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, map[string]string{"station": "R1"}) {
return
}
@@ -39,10 +35,6 @@ func (web *Web) allianceStationDisplayHandler(w http.ResponseWriter, r *http.Req
// The websocket endpoint for the alliance station display client to receive status updates.
func (web *Web) allianceStationDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -13,10 +13,6 @@ import (
// Renders the announcer display which shows team info and scores for the current match.
func (web *Web) announcerDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, nil) {
return
}
@@ -39,10 +35,6 @@ func (web *Web) announcerDisplayHandler(w http.ResponseWriter, r *http.Request)
// The websocket endpoint for the announcer display client to send control commands and receive status updates.
func (web *Web) announcerDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -31,10 +31,6 @@ type RankingWithNickname struct {
// Generates a JSON dump of the matches and results.
func (web *Web) matchesApiHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
vars := mux.Vars(r)
matches, err := web.arena.Database.GetMatchesByType(vars["type"])
if err != nil {
@@ -75,10 +71,6 @@ func (web *Web) matchesApiHandler(w http.ResponseWriter, r *http.Request) {
// Generates a JSON dump of the sponsor slides for use by the audience display.
func (web *Web) sponsorSlidesApiHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
sponsors, err := web.arena.Database.GetAllSponsorSlides()
if err != nil {
handleWebErr(w, err)
@@ -105,10 +97,6 @@ func (web *Web) sponsorSlidesApiHandler(w http.ResponseWriter, r *http.Request)
// Generates a JSON dump of the qualification rankings, primarily for use by the pit display.
func (web *Web) rankingsApiHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
rankings, err := web.arena.Database.GetAllRankings()
if err != nil {
handleWebErr(w, err)
@@ -169,10 +157,6 @@ func (web *Web) rankingsApiHandler(w http.ResponseWriter, r *http.Request) {
// Generates a JSON dump of the alliances.
func (web *Web) alliancesApiHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
alliances, err := web.arena.Database.GetAllAlliances()
if err != nil {
handleWebErr(w, err)

View File

@@ -14,10 +14,6 @@ import (
// Renders the audience display to be chroma keyed over the video feed.
func (web *Web) audienceDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, map[string]string{"background": "#0f0", "reversed": "false",
"overlayLocation": "bottom"}) {
return
@@ -42,10 +38,6 @@ func (web *Web) audienceDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the audience display client to receive status updates.
func (web *Web) audienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -13,10 +13,6 @@ import (
// Renders the field monitor display.
func (web *Web) fieldMonitorDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, map[string]string{"reversed": "false"}) {
return
}
@@ -38,10 +34,6 @@ func (web *Web) fieldMonitorDisplayHandler(w http.ResponseWriter, r *http.Reques
// The websocket endpoint for the field monitor display client to receive status updates.
func (web *Web) fieldMonitorDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -10,55 +10,35 @@ import (
func TestLoginDisplay(t *testing.T) {
web := setupTestWeb(t)
web.arena.EventSettings.ReaderPassword = "reader"
web.arena.EventSettings.AdminPassword = "admin"
// Check that hitting a reader-level protected page redirects to the login.
recorder := web.getHttpResponse("/api/alliances")
// Check that hitting a protected page redirects to the login.
recorder := web.getHttpResponse("/match_play")
assert.Equal(t, 307, recorder.Code)
assert.Equal(t, "/login?redirect=/api/alliances", recorder.Header().Get("Location"))
assert.Equal(t, "/login?redirect=/match_play", recorder.Header().Get("Location"))
recorder = web.getHttpResponse("/login?redirect=/api/alliances")
recorder = web.getHttpResponse("/login?redirect=/match_play")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "Log In - Untitled Event - Cheesy Arena")
// Check logging in with the wrong username and right password.
recorder = web.postHttpResponse("/login?redirect=/api/alliances", "username=blorpy&password=reader")
recorder = web.postHttpResponse("/login?redirect=/match_play", "username=blorpy&password=reader")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "Bad username or password")
// Check logging in with the right username and wrong password.
recorder = web.postHttpResponse("/login?redirect=/api/alliances", "username=reader&password=blorpy")
recorder = web.postHttpResponse("/login?redirect=/match_play", "username=admin&password=blorpy")
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "Bad username or password")
// Check logging in with the right username and password.
recorder = web.postHttpResponse("/login?redirect=/api/alliances", "username=reader&password=reader")
recorder = web.postHttpResponse("/login?redirect=/match_play", "username=admin&password=admin")
assert.Equal(t, 303, recorder.Code)
assert.Equal(t, "/api/alliances", recorder.Header().Get("Location"))
assert.Equal(t, "/match_play", recorder.Header().Get("Location"))
cookie := recorder.Header().Get("Set-Cookie")
assert.Contains(t, cookie, "Authorization=")
// Check that hitting the reader-level protected page works now.
recorder = web.getHttpResponseWithHeaders("/api/alliances", map[string]string{"Cookie": cookie})
assert.Equal(t, 200, recorder.Code)
// Check that hitting a admin-level protected at a higher level requires a different login.
recorder = web.getHttpResponseWithHeaders("/match_play", map[string]string{"Cookie": cookie})
assert.Equal(t, 307, recorder.Code)
assert.Equal(t, "/login?redirect=/match_play", recorder.Header().Get("Location"))
recorder = web.getHttpResponseWithHeaders("/login?redirect=/match_play", map[string]string{"Cookie": cookie})
assert.Equal(t, 200, recorder.Code)
assert.Contains(t, recorder.Body.String(), "insufficient privileges")
recorder = web.postHttpResponse("/login?redirect=/match_play", "username=admin&password=admin")
assert.Equal(t, 303, recorder.Code)
assert.Equal(t, "/match_play", recorder.Header().Get("Location"))
cookie = recorder.Header().Get("Set-Cookie")
assert.Contains(t, cookie, "Authorization=")
recorder = web.getHttpResponseWithHeaders("/match_play", map[string]string{"Cookie": cookie})
assert.Equal(t, 200, recorder.Code)
// Check that the admin user also has access to the reader-level pages.
recorder = web.getHttpResponseWithHeaders("/api/alliances", map[string]string{"Cookie": cookie})
assert.Equal(t, 200, recorder.Code)
}

View File

@@ -26,10 +26,6 @@ type MatchReviewListItem struct {
// Shows the match review interface.
func (web *Web) matchReviewHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
practiceMatches, err := web.buildMatchReviewList("practice")
if err != nil {
handleWebErr(w, err)

View File

@@ -13,10 +13,6 @@ import (
// Renders the pit display which shows scrolling rankings.
func (web *Web) pitDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, map[string]string{"scrollMsPerRow": "1000"}) {
return
}
@@ -38,10 +34,6 @@ func (web *Web) pitDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the pit display, used only to force reloads remotely.
func (web *Web) pitDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -13,10 +13,6 @@ import (
// Shows a random ID to visually identify the display so that it can be configured on the server.
func (web *Web) placeholderDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, nil) {
return
}
@@ -38,10 +34,6 @@ func (web *Web) placeholderDisplayHandler(w http.ResponseWriter, r *http.Request
// The websocket endpoint for sending configuration commands to the display.
func (web *Web) placeholderDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -16,10 +16,6 @@ const numMatchesToShow = 5
// Renders the queueing display that shows upcoming matches and timing information.
func (web *Web) queueingDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, nil) {
return
}
@@ -61,10 +57,6 @@ func (web *Web) queueingDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for the queueing display to receive updates.
func (web *Web) queueingDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -16,10 +16,6 @@ import (
// Generates a CSV-formatted report of the qualification rankings.
func (web *Web) rankingsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
rankings, err := web.arena.Database.GetAllRankings()
if err != nil {
handleWebErr(w, err)
@@ -42,10 +38,6 @@ func (web *Web) rankingsCsvReportHandler(w http.ResponseWriter, r *http.Request)
// Generates a PDF-formatted report of the qualification rankings.
func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
rankings, err := web.arena.Database.GetAllRankings()
if err != nil {
handleWebErr(w, err)
@@ -104,10 +96,6 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request)
// Generates a CSV-formatted report of the match schedule.
func (web *Web) scheduleCsvReportHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
vars := mux.Vars(r)
matches, err := web.arena.Database.GetMatchesByType(vars["type"])
if err != nil {
@@ -131,10 +119,6 @@ func (web *Web) scheduleCsvReportHandler(w http.ResponseWriter, r *http.Request)
// Generates a PDF-formatted report of the match schedule.
func (web *Web) schedulePdfReportHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
vars := mux.Vars(r)
matches, err := web.arena.Database.GetMatchesByType(vars["type"])
if err != nil {
@@ -247,10 +231,6 @@ func (web *Web) schedulePdfReportHandler(w http.ResponseWriter, r *http.Request)
// Generates a CSV-formatted report of the team list.
func (web *Web) teamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
teams, err := web.arena.Database.GetAllTeams()
if err != nil {
handleWebErr(w, err)
@@ -273,10 +253,6 @@ func (web *Web) teamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
// Generates a PDF-formatted report of the team list.
func (web *Web) teamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
teams, err := web.arena.Database.GetAllTeams()
if err != nil {
handleWebErr(w, err)

View File

@@ -65,7 +65,6 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) {
eventSettings.SwitchPassword = r.PostFormValue("switchPassword")
eventSettings.PlcAddress = r.PostFormValue("plcAddress")
eventSettings.AdminPassword = r.PostFormValue("adminPassword")
eventSettings.ReaderPassword = r.PostFormValue("readerPassword")
eventSettings.HabDockingThreshold, _ = strconv.Atoi(r.PostFormValue("habDockingThreshold"))
err := web.arena.Database.SaveEventSettings(eventSettings)

View File

@@ -13,10 +13,6 @@ import (
// Renders the Twitch stream view.
func (web *Web) twitchDisplayHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
if !web.enforceDisplayConfiguration(w, r, map[string]string{"channel": "team254"}) {
return
}
@@ -38,10 +34,6 @@ func (web *Web) twitchDisplayHandler(w http.ResponseWriter, r *http.Request) {
// The websocket endpoint for sending configuration commands to the display.
func (web *Web) twitchDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsReader(w, r) {
return
}
display, err := web.registerDisplay(r)
if err != nil {
handleWebErr(w, err)

View File

@@ -18,8 +18,7 @@ import (
)
const (
adminUser = "admin"
readerUser = "reader"
adminUser = "admin"
)
type Web struct {
@@ -108,29 +107,8 @@ func (web *Web) userIsAdmin(w http.ResponseWriter, r *http.Request) bool {
}
}
// Returns true if the given user is authorized for read-only operations. Used for HTTP cookie authentication.
func (web *Web) userIsReader(w http.ResponseWriter, r *http.Request) bool {
if web.arena.EventSettings.ReaderPassword == "" {
// Disable auth if there is no password configured.
return true
}
if username := web.cookieAuth.Authorize(r); username == readerUser || username == adminUser {
return true
} else {
http.Redirect(w, r, "/login?redirect="+r.URL.Path, 307)
return false
}
}
func (web *Web) checkAuthPassword(user, password string) bool {
switch user {
case adminUser:
return password == web.arena.EventSettings.AdminPassword
case readerUser:
return password == web.arena.EventSettings.ReaderPassword
default:
return false
}
return user == adminUser && password == web.arena.EventSettings.AdminPassword
}
// Sets up the mapping between URLs and handlers.