summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--assets/templates/authenticate.template.html8
-rw-r--r--assets/templates/information.template.html2
-rw-r--r--config.go11
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--handlers/passthrough.go8
-rw-r--r--handlers/portgate.go43
-rw-r--r--token.go42
8 files changed, 111 insertions, 6 deletions
diff --git a/assets/templates/authenticate.template.html b/assets/templates/authenticate.template.html
index 21f31e4..7d6d72c 100644
--- a/assets/templates/authenticate.template.html
+++ b/assets/templates/authenticate.template.html
@@ -1,3 +1,9 @@
 {{define "content"}}
-<h1>This is the authentication page...</h1>
+<h1>Authentication</h1>
+<form method="POST">
+    <label>
+        <input type="text" name="key">
+    </label>
+    <input type="submit" value="Authenticate">
+</form>
 {{end}}
\ No newline at end of file
diff --git a/assets/templates/information.template.html b/assets/templates/information.template.html
index e87885c..a881cf8 100644
--- a/assets/templates/information.template.html
+++ b/assets/templates/information.template.html
@@ -1,3 +1,3 @@
 {{define "content"}}
-<h1>This is the information page...</h1>
+<h1>You are authenticated!</h1>
 {{end}}
\ No newline at end of file
diff --git a/config.go b/config.go
index b4e0754..7630e87 100644
--- a/config.go
+++ b/config.go
@@ -13,6 +13,10 @@ type Config struct {
 
 	allowedPorts   []int
 	forbiddenPorts []int
+
+	key string
+
+	jwtSecret string
 }
 
 // GetConfig creates the Portgate config from outside sources such as
@@ -25,6 +29,8 @@ func GetConfig() (Config, error) {
 
 		allowedPorts:   []int{80},
 		forbiddenPorts: []int{},
+
+		key: "password",
 	}, nil
 }
 
@@ -43,3 +49,8 @@ func (c *Config) MakeUrl(p Path) string {
 	// TODO: Figure out what to do with TLS
 	return fmt.Sprintf("http://%s:%d%s", c.targetHost, p.DestinationIdentifier, p.ResourcePath)
 }
+
+// CheckKey checks whether the givenKey matches the one in the config.
+func (c *Config) CheckKey(givenKey string) bool {
+	return c.key == givenKey
+}
diff --git a/go.mod b/go.mod
index 6ba1ccd..122ac96 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.16
 
 require (
 	github.com/valyala/fasthttp v1.28.0
+	github.com/golang-jwt/jwt v3.2.1+incompatible
 
 	github.com/andybalholm/brotli v1.0.3 // indirect
 	github.com/klauspost/compress v1.13.1 // indirect
diff --git a/go.sum b/go.sum
index c912579..0a7383f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,8 @@
 github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
 github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM=
 github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
+github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
 github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ=
diff --git a/handlers/passthrough.go b/handlers/passthrough.go
index b2daa88..3f8aafc 100644
--- a/handlers/passthrough.go
+++ b/handlers/passthrough.go
@@ -2,6 +2,7 @@ package handlers
 
 import (
 	"github.com/valyala/fasthttp"
+	"net/http"
 	"portgate"
 )
 
@@ -9,9 +10,14 @@ import (
 // If the user is authorized they are allowed to pass, otherwise they should be redirected to
 // the authentication page. (/_portgate)
 func (h *RequestHandler) handlePassthroughRequest(ctx *fasthttp.RequestCtx, p portgate.Path) {
-	// TODO: Check authorization.
 	// TODO: Check whether port is allowed to be accessed.
 
+	// Check whether given cookie is ok, if not redirect to the authentication page.
+	if !portgate.VerifyTokenFromCookie(h.config, ctx) {
+		ctx.Redirect("/_portgate", http.StatusTemporaryRedirect)
+		return
+	}
+
 	// We reuse the request given to us by the user with minor changes to route it to the
 	// destination host.
 	ctx.Request.SetRequestURI(h.config.MakeUrl(p))
diff --git a/handlers/portgate.go b/handlers/portgate.go
index f53f3c9..6e002e7 100644
--- a/handlers/portgate.go
+++ b/handlers/portgate.go
@@ -2,7 +2,9 @@ package handlers
 
 import (
 	"github.com/valyala/fasthttp"
+	"net/http"
 	"portgate"
+	"time"
 )
 
 // handlePortgateRequest handles all Portgate specific request for either showing Portgate
@@ -32,14 +34,49 @@ func (h *RequestHandler) handlePortgateIndexRequest(ctx *fasthttp.RequestCtx) {
 // handlePortgatePageRequest renders the Portgate page with either the authentication page or
 // a basic information page.
 func (h *RequestHandler) handlePortgatePageRequest(ctx *fasthttp.RequestCtx) {
-	// We render the page template and pass it to the user.
 	ctx.Response.Header.SetContentType("text/html")
-	err := h.templates.ExecuteTemplate(ctx, "authenticate.template.html", nil)
+
+	var err error
+
+	// We render the page template and pass it to the user.
+	if portgate.VerifyTokenFromCookie(h.config, ctx) {
+		// User is authenticated, show the information page
+		err = h.templates.ExecuteTemplate(ctx, "information.template.html", nil)
+	} else {
+		// Show the authentication page
+		err = h.templates.ExecuteTemplate(ctx, "authenticate.template.html", nil)
+	}
+
 	if err != nil {
 		h.handleError(ctx)
 	}
 }
 
 func (h *RequestHandler) handleAuthenticateRequest(ctx *fasthttp.RequestCtx) {
-	// TODO
+
+	givenKey := ctx.PostArgs().Peek("key")
+	if givenKey == nil || !h.config.CheckKey(string(givenKey)) {
+		ctx.Error("Wrong key.", http.StatusUnauthorized)
+		return
+	}
+
+	token, err := portgate.CreateToken(h.config, string(givenKey))
+	if err != nil {
+		h.handleError(ctx)
+	}
+
+	cookie := fasthttp.AcquireCookie()
+	defer fasthttp.ReleaseCookie(cookie)
+
+	cookie.SetExpire(portgate.GetExpirationDateFrom(time.Now()))
+	cookie.SetSameSite(fasthttp.CookieSameSiteStrictMode)
+	cookie.SetHTTPOnly(true)
+	cookie.SetKey("_portgate_token")
+	cookie.SetValue(token)
+
+	ctx.Response.Header.SetCookie(cookie)
+
+	// TODO: Redirect to previously request path.
+	// http.StatusFound redirects a POST request to a GET request.
+	ctx.Redirect("/_portgate", http.StatusFound)
 }
diff --git a/token.go b/token.go
new file mode 100644
index 0000000..1031e7c
--- /dev/null
+++ b/token.go
@@ -0,0 +1,42 @@
+package portgate
+
+import (
+	"github.com/golang-jwt/jwt"
+	"github.com/valyala/fasthttp"
+	"time"
+)
+
+func CreateToken(config *Config, givenKey string) (string, error) {
+	// Our token will last 7 days
+	token := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.StandardClaims{
+		ExpiresAt: GetExpirationDateFrom(time.Now()).Unix(),
+		IssuedAt:  time.Now().Unix(),
+	})
+
+	return token.SignedString([]byte(config.jwtSecret))
+}
+
+func VerifyToken(config *Config, tokenString string) (bool, error) {
+	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
+		return []byte(config.jwtSecret), nil
+	})
+
+	if err == nil && token.Valid {
+		return true, nil
+	} else {
+		return false, err
+	}
+}
+
+func VerifyTokenFromCookie(config *Config, ctx *fasthttp.RequestCtx) bool {
+	cookie := ctx.Request.Header.Cookie("_portgate_token")
+	if cookie != nil {
+		ok, _ := VerifyToken(config, string(cookie))
+		return ok
+	}
+	return false
+}
+
+func GetExpirationDateFrom(date time.Time) time.Time {
+	return date.AddDate(0, 0, 7)
+}