Compare commits

...

1 Commits

Author SHA1 Message Date
95a17597cf added glob matching to auth middleware 2026-02-01 19:55:04 +11:00
5 changed files with 41 additions and 5 deletions

View File

@@ -9,6 +9,7 @@ import (
"git.haelnorr.com/h/golib/hlog" "git.haelnorr.com/h/golib/hlog"
"git.haelnorr.com/h/golib/hws" "git.haelnorr.com/h/golib/hws"
"git.haelnorr.com/h/golib/jwt" "git.haelnorr.com/h/golib/jwt"
"github.com/gobwas/glob"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -16,7 +17,7 @@ type Authenticator[T Model, TX DBTransaction] struct {
tokenGenerator *jwt.TokenGenerator tokenGenerator *jwt.TokenGenerator
load LoadFunc[T, TX] load LoadFunc[T, TX]
beginTx BeginTX beginTx BeginTX
ignoredPaths []string ignoredPaths []glob.Glob
logger *hlog.Logger logger *hlog.Logger
server *hws.Server server *hws.Server
errorPage hws.ErrorPageFunc errorPage hws.ErrorPageFunc

View File

@@ -16,6 +16,7 @@ require (
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect

View File

@@ -15,6 +15,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=

View File

@@ -3,6 +3,8 @@ package hwsauth
import ( import (
"fmt" "fmt"
"net/url" "net/url"
"github.com/gobwas/glob"
) )
// IgnorePaths excludes specified paths from authentication middleware. // IgnorePaths excludes specified paths from authentication middleware.
@@ -25,6 +27,19 @@ func (auth *Authenticator[T, TX]) IgnorePaths(paths ...string) error {
return fmt.Errorf("Invalid path: '%s'", path) return fmt.Errorf("Invalid path: '%s'", path)
} }
} }
auth.ignoredPaths = paths auth.ignoredPaths = prepareGlobs(paths)
return nil return nil
} }
func prepareGlobs(paths []string) []glob.Glob {
compiledGlobs := make([]glob.Glob, 0, len(paths))
for _, pattern := range paths {
g, err := glob.Compile(pattern)
if err != nil {
// If pattern fails to compile, skip it
continue
}
compiledGlobs = append(compiledGlobs, g)
}
return compiledGlobs
}

View File

@@ -3,10 +3,10 @@ package hwsauth
import ( import (
"context" "context"
"net/http" "net/http"
"slices"
"time" "time"
"git.haelnorr.com/h/golib/hws" "git.haelnorr.com/h/golib/hws"
"github.com/gobwas/glob"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -23,7 +23,7 @@ func (auth *Authenticator[T, TX]) Authenticate() hws.Middleware {
func (auth *Authenticator[T, TX]) authenticate() hws.MiddlewareFunc { func (auth *Authenticator[T, TX]) authenticate() hws.MiddlewareFunc {
return func(w http.ResponseWriter, r *http.Request) (*http.Request, *hws.HWSError) { return func(w http.ResponseWriter, r *http.Request) (*http.Request, *hws.HWSError) {
if slices.Contains(auth.ignoredPaths, r.URL.Path) { if globTest(r.URL.Path, auth.ignoredPaths) {
return r, nil return r, nil
} }
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
@@ -38,6 +38,7 @@ func (auth *Authenticator[T, TX]) authenticate() hws.MiddlewareFunc {
Error: errors.Wrap(err, "auth.beginTx"), Error: errors.Wrap(err, "auth.beginTx"),
} }
} }
defer tx.Rollback()
// Type assert to TX - safe because user's beginTx should return their TX type // Type assert to TX - safe because user's beginTx should return their TX type
txTyped, ok := tx.(TX) txTyped, ok := tx.(TX)
if !ok { if !ok {
@@ -49,7 +50,14 @@ func (auth *Authenticator[T, TX]) authenticate() hws.MiddlewareFunc {
} }
model, err := auth.getAuthenticatedUser(txTyped, w, r) model, err := auth.getAuthenticatedUser(txTyped, w, r)
if err != nil { if err != nil {
tx.Rollback() rberr := tx.Rollback()
if rberr != nil {
return nil, &hws.HWSError{
Message: "Failed rolling back after error",
StatusCode: http.StatusInternalServerError,
Error: errors.Wrap(err, "tx.Rollback"),
}
}
auth.logger.Debug(). auth.logger.Debug().
Str("remote_addr", r.RemoteAddr). Str("remote_addr", r.RemoteAddr).
Err(err). Err(err).
@@ -62,3 +70,12 @@ func (auth *Authenticator[T, TX]) authenticate() hws.MiddlewareFunc {
return newReq, nil return newReq, nil
} }
} }
func globTest(testPath string, globs []glob.Glob) bool {
for _, g := range globs {
if g.Match(testPath) {
return true
}
}
return false
}