mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-27 19:41:11 +01:00
This is a large-scale refactoring across the codebase that replaces the custom `gperr.Error` type with Go's standard `error` interface. The changes include: - Replacing `gperr.Error` return types with `error` in function signatures - Using `errors.New()` and `fmt.Errorf()` instead of `gperr.New()` and `gperr.Errorf()` - Using `%w` format verb for error wrapping instead of `.With()` method - Replacing `gperr.Subject()` calls with `gperr.PrependSubject()` - Converting error logging from `gperr.Log*()` functions to zerolog's `.Err().Msg()` pattern - Update NewLogger to handle multiline error message - Updating `goutils` submodule to latest commit This refactoring aligns with Go idioms and removes the dependency on custom error handling abstractions in favor of standard library patterns.
123 lines
2.8 KiB
Go
123 lines
2.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/yusing/godoxy/internal/auth"
|
|
)
|
|
|
|
type oidcMiddleware struct {
|
|
AllowedUsers []string `json:"allowed_users"`
|
|
AllowedGroups []string `json:"allowed_groups"`
|
|
ClientID string `json:"client_id"`
|
|
ClientSecret string `json:"client_secret"`
|
|
Scopes string `json:"scopes"`
|
|
|
|
auth *auth.OIDCProvider
|
|
|
|
isInitialized int32
|
|
initMu sync.Mutex
|
|
}
|
|
|
|
var OIDC = NewMiddleware[oidcMiddleware]()
|
|
|
|
func (amw *oidcMiddleware) finalize() error {
|
|
if !auth.IsOIDCEnabled() {
|
|
return errors.New("OIDC not enabled but OIDC middleware is used")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (amw *oidcMiddleware) init() error {
|
|
if atomic.LoadInt32(&amw.isInitialized) == 1 {
|
|
return nil
|
|
}
|
|
|
|
return amw.initSlow()
|
|
}
|
|
|
|
func (amw *oidcMiddleware) initSlow() error {
|
|
amw.initMu.Lock()
|
|
if amw.isInitialized == 1 {
|
|
amw.initMu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
defer func() {
|
|
amw.isInitialized = 1
|
|
amw.initMu.Unlock()
|
|
}()
|
|
|
|
// Always start with the global OIDC provider (for issuer discovery)
|
|
authProvider, err := auth.NewOIDCProviderFromEnv()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check if custom client credentials are provided
|
|
if amw.ClientID != "" && amw.ClientSecret != "" {
|
|
// Use custom client credentials
|
|
customProvider, err := auth.NewOIDCProviderWithCustomClient(
|
|
authProvider,
|
|
amw.ClientID,
|
|
amw.ClientSecret,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
authProvider = customProvider
|
|
}
|
|
// If no custom credentials, authProvider remains the global one
|
|
|
|
// Always trigger login on unknown paths.
|
|
// This prevents falling back to the default login page, which applies bypass rules.
|
|
// Without this, redirecting to the global login page could circumvent the intended route restrictions.
|
|
authProvider.SetOnUnknownPathHandler(authProvider.LoginHandler)
|
|
|
|
// Apply per-route user/group restrictions (these always override global)
|
|
if len(amw.AllowedUsers) > 0 {
|
|
authProvider.SetAllowedUsers(amw.AllowedUsers)
|
|
}
|
|
if len(amw.AllowedGroups) > 0 {
|
|
authProvider.SetAllowedGroups(amw.AllowedGroups)
|
|
}
|
|
|
|
// Apply custom scopes if provided
|
|
if amw.Scopes != "" {
|
|
authProvider.SetScopes(strings.Split(amw.Scopes, ","))
|
|
}
|
|
|
|
amw.auth = authProvider
|
|
return nil
|
|
}
|
|
|
|
func (amw *oidcMiddleware) before(w http.ResponseWriter, r *http.Request) (proceed bool) {
|
|
if err := amw.init(); err != nil {
|
|
// no need to log here, main OIDC should've already failed and logged
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return false
|
|
}
|
|
|
|
if r.URL.Path == auth.OIDCLogoutPath {
|
|
amw.auth.LogoutHandler(w, r)
|
|
return true
|
|
}
|
|
|
|
err := amw.auth.CheckToken(r)
|
|
if err == nil {
|
|
return true
|
|
}
|
|
|
|
switch {
|
|
case errors.Is(err, auth.ErrMissingOAuthToken):
|
|
amw.auth.HandleAuth(w, r)
|
|
default:
|
|
auth.WriteBlockPage(w, http.StatusForbidden, err.Error(), "Logout", auth.OIDCLogoutPath)
|
|
}
|
|
return false
|
|
}
|