Browse Source

Merge pull request #16 from msoedov/pr14

#14 Optional basic auth
Alex Myasoedov 6 years ago
parent
commit
c99e8841fb
2 changed files with 64 additions and 1 deletions
  1. 16 1
      Readme.md
  2. 48 0
      main.go

+ 16 - 1
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
 ------------

+ 48 - 0
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")