From 3a239a95c34760cf29a48e09545be91bc3100b80 Mon Sep 17 00:00:00 2001 From: Trong Huu Nguyen Date: Sat, 29 Apr 2023 11:54:53 +0200 Subject: [PATCH] feat(reverseproxy): validate acr and redirect if applicable --- pkg/handler/acr/acr.go | 27 ++++++++++++++++++++++++ pkg/handler/handler.go | 11 ++++++++-- pkg/handler/handler_sso_proxy.go | 11 ++++++++-- pkg/handler/reverseproxy.go | 36 +++++++++++++++++++++----------- pkg/session/session.go | 4 ++++ 5 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 pkg/handler/acr/acr.go diff --git a/pkg/handler/acr/acr.go b/pkg/handler/acr/acr.go new file mode 100644 index 0000000..a7af7d9 --- /dev/null +++ b/pkg/handler/acr/acr.go @@ -0,0 +1,27 @@ +package acr + +import ( + "github.com/nais/wonderwall/pkg/config" + "github.com/nais/wonderwall/pkg/openid" + "github.com/nais/wonderwall/pkg/session" +) + +type Handler struct { + Enabled bool + ExpectedValue string +} + +func (h *Handler) Validate(sess *session.Session) error { + if !h.Enabled || sess == nil { + return nil + } + + return openid.ValidateAcr(h.ExpectedValue, sess.Acr()) +} + +func NewHandler(cfg *config.Config) *Handler { + return &Handler{ + Enabled: len(cfg.OpenID.ACRValues) > 0, + ExpectedValue: cfg.OpenID.ACRValues, + } +} diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 4041e85..7121ea2 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -15,6 +15,7 @@ import ( "github.com/nais/wonderwall/pkg/config" "github.com/nais/wonderwall/pkg/cookie" "github.com/nais/wonderwall/pkg/crypto" + "github.com/nais/wonderwall/pkg/handler/acr" "github.com/nais/wonderwall/pkg/handler/autologin" "github.com/nais/wonderwall/pkg/ingress" "github.com/nais/wonderwall/pkg/metrics" @@ -31,6 +32,7 @@ import ( var _ router.Source = &Standalone{} type Standalone struct { + AcrHandler *acr.Handler AutoLogin *autologin.AutoLogin Client *openidclient.Client Config *config.Config @@ -77,6 +79,7 @@ func NewStandalone( } return &Standalone{ + AcrHandler: acr.NewHandler(cfg), AutoLogin: autoLogin, Client: openidClient, Config: cfg, @@ -90,8 +93,8 @@ func NewStandalone( }, nil } -func (s *Standalone) GetSession(r *http.Request) (*session.Session, error) { - return s.SessionManager.GetOrRefresh(r) +func (s *Standalone) GetAcrHandler() *acr.Handler { + return s.AcrHandler } func (s *Standalone) GetAutoLogin() *autologin.AutoLogin { @@ -115,6 +118,10 @@ func (s *Standalone) GetPath(r *http.Request) string { return GetPath(r, s.GetIngresses()) } +func (s *Standalone) GetSession(r *http.Request) (*session.Session, error) { + return s.SessionManager.GetOrRefresh(r) +} + func (s *Standalone) Login(w http.ResponseWriter, r *http.Request) { canonicalRedirect := s.Redirect.Canonical(r) login, err := s.Client.Login(r) diff --git a/pkg/handler/handler_sso_proxy.go b/pkg/handler/handler_sso_proxy.go index 60b1729..859db74 100644 --- a/pkg/handler/handler_sso_proxy.go +++ b/pkg/handler/handler_sso_proxy.go @@ -9,6 +9,7 @@ import ( "github.com/nais/wonderwall/pkg/config" "github.com/nais/wonderwall/pkg/crypto" + "github.com/nais/wonderwall/pkg/handler/acr" "github.com/nais/wonderwall/pkg/handler/autologin" "github.com/nais/wonderwall/pkg/ingress" logentry "github.com/nais/wonderwall/pkg/middleware" @@ -22,6 +23,7 @@ import ( var _ router.Source = &SSOProxy{} type SSOProxy struct { + AcrHandler *acr.Handler AutoLogin *autologin.AutoLogin Config *config.Config Ingresses *ingress.Ingresses @@ -59,6 +61,7 @@ func NewSSOProxy(cfg *config.Config, crypter crypto.Crypter) (*SSOProxy, error) } return &SSOProxy{ + AcrHandler: acr.NewHandler(cfg), AutoLogin: autoLogin, Config: cfg, Ingresses: ingresses, @@ -70,8 +73,8 @@ func NewSSOProxy(cfg *config.Config, crypter crypto.Crypter) (*SSOProxy, error) }, nil } -func (s *SSOProxy) GetSession(r *http.Request) (*session.Session, error) { - return s.SessionReader.Get(r) +func (s *SSOProxy) GetAcrHandler() *acr.Handler { + return s.AcrHandler } func (s *SSOProxy) GetAutoLogin() *autologin.AutoLogin { @@ -86,6 +89,10 @@ func (s *SSOProxy) GetPath(r *http.Request) string { return GetPath(r, s.GetIngresses()) } +func (s *SSOProxy) GetSession(r *http.Request) (*session.Session, error) { + return s.SessionReader.Get(r) +} + func (s *SSOProxy) GetSSOServerURL() *urllib.URL { u := *s.SSOServerURL return &u diff --git a/pkg/handler/reverseproxy.go b/pkg/handler/reverseproxy.go index c4ba204..8fa1aac 100644 --- a/pkg/handler/reverseproxy.go +++ b/pkg/handler/reverseproxy.go @@ -10,6 +10,7 @@ import ( "github.com/sirupsen/logrus" + "github.com/nais/wonderwall/pkg/handler/acr" "github.com/nais/wonderwall/pkg/handler/autologin" mw "github.com/nais/wonderwall/pkg/middleware" "github.com/nais/wonderwall/pkg/session" @@ -17,6 +18,7 @@ import ( ) type ReverseProxySource interface { + GetAcrHandler() *acr.Handler GetAutoLogin() *autologin.AutoLogin GetPath(r *http.Request) string GetSession(r *http.Request) (*session.Session, error) @@ -65,7 +67,7 @@ func (rp *ReverseProxy) Handler(src ReverseProxySource, w http.ResponseWriter, r logger := mw.LogEntryFrom(r) isAuthenticated := false - _, accessToken, err := getSessionWithValidToken(src, r) + sess, accessToken, err := getSessionWithValidToken(src, r) switch { case err == nil: // add authentication if session checks out @@ -82,18 +84,14 @@ func (rp *ReverseProxy) Handler(src ReverseProxySource, w http.ResponseWriter, r logger.Errorf("default: unauthenticated: unexpected error: %+v", err) } + err = src.GetAcrHandler().Validate(sess) + if err != nil { + loginRedirect(src, w, r, err.Error()) + return + } + if src.GetAutoLogin().NeedsLogin(r, isAuthenticated) { - redirectTarget := r.URL.String() - path := src.GetPath(r) - - loginUrl := url.LoginRelative(path, redirectTarget) - fields := logrus.Fields{ - "redirect_after_login": redirectTarget, - "redirect_to": loginUrl, - } - - logger.WithFields(fields).Info("default: unauthenticated: request matches auto-login; redirecting to login...") - http.Redirect(w, r, loginUrl, http.StatusFound) + loginRedirect(src, w, r, "request matches autologin") return } @@ -120,6 +118,20 @@ func getSessionWithValidToken(src ReverseProxySource, r *http.Request) (*session return sess, accessToken, nil } +func loginRedirect(src ReverseProxySource, w http.ResponseWriter, r *http.Request, message string) { + redirectTarget := r.URL.String() + path := src.GetPath(r) + + loginUrl := url.LoginRelative(path, redirectTarget) + fields := logrus.Fields{ + "redirect_after_login": redirectTarget, + "redirect_to": loginUrl, + } + + mw.LogEntryFrom(r).WithFields(fields).Infof("default: unauthenticated: %s; redirecting to login...", message) + http.Redirect(w, r, loginUrl, http.StatusFound) +} + type logrusErrorWriter struct{} func (w logrusErrorWriter) Write(p []byte) (n int, err error) { diff --git a/pkg/session/session.go b/pkg/session/session.go index bba9a12..07ed8b8 100644 --- a/pkg/session/session.go +++ b/pkg/session/session.go @@ -60,6 +60,10 @@ func (in *Session) AccessToken() (string, error) { return "", fmt.Errorf("%w: access token is expired", ErrInvalid) } +func (in *Session) Acr() string { + return in.data.Acr +} + func (in *Session) ExternalSessionID() string { return in.data.ExternalSessionID }