From dd3e78eb28dcf5906bf640b24e36f3f9dc3374b2 Mon Sep 17 00:00:00 2001 From: Mike Shoup Date: Sat, 13 Oct 2018 09:32:13 -0600 Subject: [PATCH] Succesfully redirect if 401 --- auth.go | 76 +++++++++++++++++++++++++++++++++++++++++++ html/js/thermostat.js | 4 +-- web.go | 5 +-- 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 auth.go diff --git a/auth.go b/auth.go new file mode 100644 index 0000000..dcdd167 --- /dev/null +++ b/auth.go @@ -0,0 +1,76 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// Modified to remove the WWW-Authenticate header for uses in TempGopher + +package main + +import ( + "encoding/base64" + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +type authPair struct { + value string + user string +} + +type authPairs []authPair + +func (a authPairs) searchCredential(authValue string) (string, bool) { + if authValue == "" { + return "", false + } + for _, pair := range a { + if pair.value == authValue { + return pair.user, true + } + } + return "", false +} + +// BasicAuth returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where +// the key is the user name and the value is the password. This does not set a www-authenticate header. +func BasicAuth(accounts gin.Accounts) gin.HandlerFunc { + pairs := processAccounts(accounts) + return func(c *gin.Context) { + // Search user in the slice of allowed credentials + user, found := pairs.searchCredential(c.GetHeader("Authorization")) + if !found { + // Credentials doesn't match, we return 401 and abort handlers chain. + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using + // c.MustGet(gin.AuthUserKey). + c.Set(gin.AuthUserKey, user) + } +} + +func processAccounts(accounts gin.Accounts) authPairs { + if len(accounts) == 0 { + log.Panic("Empty list of authorized credentials") + } + pairs := make(authPairs, 0, len(accounts)) + for user, password := range accounts { + if user == "" { + log.Panic("User can not be empty") + } + value := authorizationHeader(user, password) + pairs = append(pairs, authPair{ + value: value, + user: user, + }) + } + return pairs +} + +func authorizationHeader(user, password string) string { + base := user + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString([]byte(base)) +} diff --git a/html/js/thermostat.js b/html/js/thermostat.js index e2138e1..2be281d 100644 --- a/html/js/thermostat.js +++ b/html/js/thermostat.js @@ -12,10 +12,10 @@ function redirectIfNotAuthorized() { beforeSend: authHeaders, statusCode: { 401: function() { - window.location.replace(baseurl + "/app/login.html"); + window.location.replace(jsconfig.baseurl + "/app/login.html"); }, 403: function() { - window.location.replace(baseurl + "/app/login.html"); + window.location.replace(jsconfig.baseurl + "/app/login.html"); } } }); diff --git a/web.go b/web.go index da3c8ae..2d33d1e 100644 --- a/web.go +++ b/web.go @@ -130,7 +130,8 @@ func SetupRouter(config *Config, states *map[string]State) *gin.Engine { if len(config.Users) == 0 { api = r.Group("/api") } else { - api = r.Group("/api", gin.BasicAuth(GetGinAccounts(config))) + api = r.Group("/api") + api.Use(BasicAuth(GetGinAccounts(config))) } api.GET("/status", StatusHandler(states)) @@ -166,7 +167,7 @@ func reloadWebConfig(c *Config, p string) error { // GetGinAccounts returns a gin.Accounts struct with values pulled from a Config struct func GetGinAccounts(config *Config) gin.Accounts { - var a gin.Accounts + a := make(gin.Accounts) for _, user := range config.Users { a[user.Name] = user.Password }