diff --git a/Readme.md b/Readme.md index d72aa0e..de222a8 100644 --- a/Readme.md +++ b/Readme.md @@ -16,6 +16,7 @@ This repo is a reworked version of [Sandstorm Hacker Slides](https://github.com/ - Pdf print - [Demo version](https://murmuring-sierra-54081.herokuapp.com) - Tiny 10 Mb docker image +- Optional Basic auth | Edit mode | Published | @@ -45,9 +46,23 @@ And then you can just open [http://127.0.0.1:8080](http://127.0.0.1:8080) and it Run with docker ```shell -docker run -it -p 8080:8080 -v $(pwd)/slides:/app/slides msoedov/hacker-slides +docker run -it -p 8080:8080 -v $(pwd)/slides:/app/slides msoedov/hacker-slides ``` +Basic auth (disabled by default) +```shell +USER=bob PASSWORD=password1 go run main.go +[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. + - using env: export GIN_MODE=release + - using code: gin.SetMode(gin.ReleaseMode) + +WARN[0000] Auth mode enabled +WARN[0000] Visit http://bob:password1@0.0.0.0:8080 +``` + +```shell +docker run -it -p 8080:8080 -e USER=bob -e PASSWORD=password1 -v $(pwd)/slides:/app/slides msoedov/hacker-slides +``` Getting Help ------------ diff --git a/main.go b/main.go index f3a0aa0..39707ac 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,12 @@ package main import ( + "encoding/base64" "errors" "fmt" "io/ioutil" "os" + "strconv" "strings" log "github.com/Sirupsen/logrus" @@ -15,12 +17,58 @@ import ( const sessionHeader = "slide-session" +func Header(c *gin.Context, key string) string { + if values, _ := c.Request.Header[key]; len(values) > 0 { + return values[0] + } + return "" +} + +func BasicAuth() gin.HandlerFunc { + realm := "Authorization Required" + realm = "Basic realm=" + strconv.Quote(realm) + user := os.Getenv("USER") + password := os.Getenv("PASSWORD") + enabled := isEnabled(user, password) + if enabled { + log.Warn("Auth mode enabled") + log.Warn(fmt.Sprintf("Visit http://%s:%s@0.0.0.0:8080", user, password)) + } + return func(c *gin.Context) { + header := Header(c, "Authorization") + if enabled && header != authorizationHeader(user, password) { + // Credentials doesn't match, we return 401 and abort handlers chain. + c.Header("WWW-Authenticate", realm) + c.AbortWithStatus(401) + return + } + c.Next() + } +} + +func isEnabled(user, password string) bool { + switch { + case user == "": + return false + case password == "": + return false + default: + return true + } +} + +func authorizationHeader(user, password string) string { + base := user + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString([]byte(base)) +} + func NewApp() *gin.Engine { r := gin.Default() store := sessions.NewCookieStore([]byte("secret")) r.Use(sessions.Sessions(sessionHeader, store)) + r.Use(BasicAuth()) r.LoadHTMLGlob("templates/*.tmpl") r.Static("/static", "./static")