summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-03-30 01:49:14 +0700
committerShulhan <ms@kilabit.info>2022-03-30 01:49:14 +0700
commit8a8de54d66fd14d1fbdb7e8598c597f871230980 (patch)
tree1104e23c761563502a2f05be2ef1cda3e0fe12f6
parent0c288a5880fbc0fceb792dcfd0747d95163a90dc (diff)
downloadpakakeh.go-8a8de54d66fd14d1fbdb7e8598c597f871230980.tar.xz
lib/http: implement handler to authorized request to Server Memfs
The FSAuthHandler in the ServerOptions define a function that will be called to each GET request to Server Memfs instance using the value from the HTTP Request instance. If the request is not authorized it must return false and the HTTP response will be set to 401 Unauthorized with empty body.
-rw-r--r--lib/http/fs_auth_handler.go16
-rw-r--r--lib/http/http_test.go31
-rw-r--r--lib/http/server.go13
-rw-r--r--lib/http/server_test.go73
-rw-r--r--lib/http/serveroptions.go4
-rw-r--r--lib/http/testdata/auth.txt1
-rw-r--r--lib/http/testdata/auth/index.html1
-rw-r--r--lib/http/testdata/auth/sub/index.html1
8 files changed, 139 insertions, 1 deletions
diff --git a/lib/http/fs_auth_handler.go b/lib/http/fs_auth_handler.go
new file mode 100644
index 00000000..9a793d64
--- /dev/null
+++ b/lib/http/fs_auth_handler.go
@@ -0,0 +1,16 @@
+// Copyright 2022, Shulhan <ms@kilabit.info>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http
+
+import "net/http"
+
+//
+// FSAuthHandler define the function to authorized each GET request to
+// Server MemFS using value from the HTTP Request instance.
+//
+// If request is not authorized it must return false and the HTTP response
+// will be set to 401 Unauthorized with empty body.
+//
+type FSAuthHandler func(req *http.Request) (ok bool)
diff --git a/lib/http/http_test.go b/lib/http/http_test.go
index 707b383f..d5eb2c44 100644
--- a/lib/http/http_test.go
+++ b/lib/http/http_test.go
@@ -9,7 +9,9 @@ import (
"log"
"net/http"
"os"
+ "strings"
"testing"
+ "time"
"github.com/shuLhan/share/lib/memfs"
)
@@ -56,7 +58,8 @@ func TestMain(m *testing.M) {
Development: true,
},
},
- Address: serverAddress,
+ HandleFSAuth: handleFSAuth,
+ Address: serverAddress,
}
testServerUrl = fmt.Sprintf("http://" + serverAddress)
@@ -75,6 +78,8 @@ func TestMain(m *testing.M) {
}
}()
+ time.Sleep(400 * time.Millisecond) // Wait for server to be ready.
+
status := m.Run()
err = testServer.Stop(0)
@@ -89,6 +94,30 @@ var (
testDownloadBody []byte
)
+// handleFSAuth authenticate the request to Memfs using cookie.
+// It will return true if request path is "/auth/" and cookie name "sid" exist
+// with value "authz".
+func handleFSAuth(req *http.Request) bool {
+ var (
+ lowerPath = strings.ToLower(req.URL.Path)
+
+ cookieSid *http.Cookie
+ err error
+ )
+ log.Printf("handleFSAuth: %s", lowerPath)
+ if !strings.HasPrefix(lowerPath, "/auth/") {
+ return true
+ }
+ cookieSid, err = req.Cookie("sid")
+ if err != nil {
+ return false
+ }
+ if cookieSid.Value != "authz" {
+ return false
+ }
+ return true
+}
+
func registerEndpoints() {
var err error
diff --git a/lib/http/server.go b/lib/http/server.go
index 4a9f4aa7..e6ef66b1 100644
--- a/lib/http/server.go
+++ b/lib/http/server.go
@@ -489,6 +489,9 @@ func (srv *Server) handleDelete(res http.ResponseWriter, req *http.Request) {
// response body set to the content of file.
// If the request Method is HEAD, only the header will be sent back to client.
//
+// If the request Path exists and Server Options FSAuthHandler is set and
+// returning false, it will return 401 Unauthorized.
+//
// If the request Path is not exist it will return 404 Not Found.
//
func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
@@ -501,6 +504,7 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
body []byte
size int64
err error
+ isAuthorized bool
)
node = srv.getFSNode(req.URL.Path)
@@ -509,6 +513,15 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
return
}
+ if srv.Options.HandleFSAuth != nil {
+ req.URL.Path = node.Path
+ isAuthorized = srv.Options.HandleFSAuth(req)
+ if !isAuthorized {
+ res.WriteHeader(http.StatusUnauthorized)
+ return
+ }
+ }
+
res.Header().Set(HeaderContentType, node.ContentType)
responseETag = strconv.FormatInt(node.ModTime().Unix(), 10)
diff --git a/lib/http/server_test.go b/lib/http/server_test.go
index 7364469d..a63bdb92 100644
--- a/lib/http/server_test.go
+++ b/lib/http/server_test.go
@@ -787,3 +787,76 @@ func TestStatusError(t *testing.T) {
test.Assert(t, "Body", c.expBody, string(body))
}
}
+
+// TestServer_HandleFS_Auth test GET on memfs with authorization.
+func TestServer_HandleFS_Auth(t *testing.T) {
+ type testCase struct {
+ cookieSid *http.Cookie
+ desc string
+ reqPath string
+ expStatusCode int
+ }
+
+ var (
+ c testCase
+ req *http.Request
+ res *http.Response
+ err error
+ )
+
+ cases := []testCase{{
+ desc: "With public path",
+ reqPath: "/index.html",
+ expStatusCode: http.StatusOK,
+ }, {
+ desc: "With /auth.txt",
+ reqPath: "/auth.txt",
+ expStatusCode: http.StatusOK,
+ }, {
+ desc: "With /auth path no cookie",
+ reqPath: "/auth",
+ expStatusCode: http.StatusUnauthorized,
+ }, {
+ desc: "With /auth path and cookie",
+ reqPath: "/auth",
+ cookieSid: &http.Cookie{
+ Name: "sid",
+ Value: "authz",
+ },
+ expStatusCode: http.StatusOK,
+ }, {
+ desc: "With invalid /auth path and cookie",
+ reqPath: "/auth/notexist",
+ cookieSid: &http.Cookie{
+ Name: "sid",
+ Value: "authz",
+ },
+ expStatusCode: http.StatusNotFound,
+ }, {
+ desc: "With /auth/sub path and cookie",
+ reqPath: "/auth/sub",
+ cookieSid: &http.Cookie{
+ Name: "sid",
+ Value: "authz",
+ },
+ expStatusCode: http.StatusOK,
+ }}
+
+ for _, c = range cases {
+ req, err = http.NewRequest(http.MethodGet, testServerUrl+c.reqPath, nil)
+ if err != nil {
+ t.Fatalf("%s: %s", c.desc, err)
+ }
+
+ if c.cookieSid != nil {
+ req.AddCookie(c.cookieSid)
+ }
+
+ res, err = client.Do(req)
+ if err != nil {
+ t.Fatalf("%s: %s", c.desc, err)
+ }
+
+ test.Assert(t, c.desc, c.expStatusCode, res.StatusCode)
+ }
+}
diff --git a/lib/http/serveroptions.go b/lib/http/serveroptions.go
index 805f4dc7..b548d09c 100644
--- a/lib/http/serveroptions.go
+++ b/lib/http/serveroptions.go
@@ -26,6 +26,10 @@ type ServerOptions struct {
// See https://pkg.go.dev/github.com/shuLhan/share/lib/memfs#hdr-Go_embed
Memfs *memfs.MemFS
+ // HandleFSAuth handle authorization to GET request on Memfs.
+ // If null it means all request is allowed.
+ HandleFSAuth FSAuthHandler
+
// Address define listen address, using ip:port format.
// This field is optional, default to ":80".
Address string
diff --git a/lib/http/testdata/auth.txt b/lib/http/testdata/auth.txt
new file mode 100644
index 00000000..69e75143
--- /dev/null
+++ b/lib/http/testdata/auth.txt
@@ -0,0 +1 @@
+Hello, auth.txt!
diff --git a/lib/http/testdata/auth/index.html b/lib/http/testdata/auth/index.html
new file mode 100644
index 00000000..4b00ac04
--- /dev/null
+++ b/lib/http/testdata/auth/index.html
@@ -0,0 +1 @@
+<html><body>Hello, authorized world!</body></html>
diff --git a/lib/http/testdata/auth/sub/index.html b/lib/http/testdata/auth/sub/index.html
new file mode 100644
index 00000000..476b228b
--- /dev/null
+++ b/lib/http/testdata/auth/sub/index.html
@@ -0,0 +1 @@
+<html><body>Hello, /auth/sub!</body></html>