Merge pull request #180 from govau/littletidyups
Minor restructure for greater confidence that only authenticated requests are proxied
This commit is contained in:
commit
0d6fa6216d
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
## Changes since v3.2.0
|
## Changes since v3.2.0
|
||||||
|
|
||||||
|
- [#180](https://github.com/pusher/outh2_proxy/pull/180) Minor refactor of core proxying path (@aeijdenberg).
|
||||||
- [#175](https://github.com/pusher/outh2_proxy/pull/175) Bump go-oidc to v2.0.0 (@aeijdenberg).
|
- [#175](https://github.com/pusher/outh2_proxy/pull/175) Bump go-oidc to v2.0.0 (@aeijdenberg).
|
||||||
- Includes fix for potential signature checking issue when OIDC discovery is skipped.
|
- Includes fix for potential signature checking issue when OIDC discovery is skipped.
|
||||||
- [#155](https://github.com/pusher/outh2_proxy/pull/155) Add RedisSessionStore implementation (@brianv0, @JoelSpeed)
|
- [#155](https://github.com/pusher/outh2_proxy/pull/155) Add RedisSessionStore implementation (@brianv0, @JoelSpeed)
|
||||||
|
@ -47,6 +47,11 @@ var SignatureHeaders = []string{
|
|||||||
"Gap-Auth",
|
"Gap-Auth",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNeedsLogin means the user should be redirected to the login page
|
||||||
|
ErrNeedsLogin = errors.New("redirect to login page")
|
||||||
|
)
|
||||||
|
|
||||||
// OAuthProxy is the main authentication proxy
|
// OAuthProxy is the main authentication proxy
|
||||||
type OAuthProxy struct {
|
type OAuthProxy struct {
|
||||||
CookieSeed string
|
CookieSeed string
|
||||||
@ -477,20 +482,19 @@ func (p *OAuthProxy) IsValidRedirect(redirect string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsWhitelistedRequest is used to check if auth should be skipped for this request
|
// IsWhitelistedRequest is used to check if auth should be skipped for this request
|
||||||
func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) (ok bool) {
|
func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) bool {
|
||||||
isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS"
|
isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS"
|
||||||
return isPreflightRequestAllowed || p.IsWhitelistedPath(req.URL.Path)
|
return isPreflightRequestAllowed || p.IsWhitelistedPath(req.URL.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsWhitelistedPath is used to check if the request path is allowed without auth
|
// IsWhitelistedPath is used to check if the request path is allowed without auth
|
||||||
func (p *OAuthProxy) IsWhitelistedPath(path string) (ok bool) {
|
func (p *OAuthProxy) IsWhitelistedPath(path string) bool {
|
||||||
for _, u := range p.compiledRegex {
|
for _, u := range p.compiledRegex {
|
||||||
ok = u.MatchString(path)
|
if u.MatchString(path) {
|
||||||
if ok {
|
return true
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRemoteAddr(req *http.Request) (s string) {
|
func getRemoteAddr(req *http.Request) (s string) {
|
||||||
@ -641,36 +645,54 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
// AuthenticateOnly checks whether the user is currently logged in
|
// AuthenticateOnly checks whether the user is currently logged in
|
||||||
func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) {
|
func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) {
|
||||||
status := p.Authenticate(rw, req)
|
session, err := p.getAuthenticatedSession(rw, req)
|
||||||
if status == http.StatusAccepted {
|
if err != nil {
|
||||||
rw.WriteHeader(http.StatusAccepted)
|
|
||||||
} else {
|
|
||||||
http.Error(rw, "unauthorized request", http.StatusUnauthorized)
|
http.Error(rw, "unauthorized request", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we are authenticated
|
||||||
|
p.addHeadersForProxying(rw, req, session)
|
||||||
|
rw.WriteHeader(http.StatusAccepted)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy proxies the user request if the user is authenticated else it prompts
|
// Proxy proxies the user request if the user is authenticated else it prompts
|
||||||
// them to authenticate
|
// them to authenticate
|
||||||
func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
||||||
status := p.Authenticate(rw, req)
|
session, err := p.getAuthenticatedSession(rw, req)
|
||||||
if status == http.StatusInternalServerError {
|
switch err {
|
||||||
p.ErrorPage(rw, http.StatusInternalServerError,
|
case nil:
|
||||||
"Internal Error", "Internal Error")
|
// we are authenticated
|
||||||
} else if status == http.StatusForbidden {
|
p.addHeadersForProxying(rw, req, session)
|
||||||
|
p.serveMux.ServeHTTP(rw, req)
|
||||||
|
|
||||||
|
case ErrNeedsLogin:
|
||||||
|
// we need to send the user to a login screen
|
||||||
|
if isAjax(req) {
|
||||||
|
// no point redirecting an AJAX request
|
||||||
|
p.ErrorJSON(rw, http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if p.SkipProviderButton {
|
if p.SkipProviderButton {
|
||||||
p.OAuthStart(rw, req)
|
p.OAuthStart(rw, req)
|
||||||
} else {
|
} else {
|
||||||
p.SignInPage(rw, req, http.StatusForbidden)
|
p.SignInPage(rw, req, http.StatusForbidden)
|
||||||
}
|
}
|
||||||
} else if status == http.StatusUnauthorized {
|
|
||||||
p.ErrorJSON(rw, status)
|
default:
|
||||||
} else {
|
// unknown error
|
||||||
p.serveMux.ServeHTTP(rw, req)
|
logger.Printf("Unexpected internal error: %s", err)
|
||||||
|
p.ErrorPage(rw, http.StatusInternalServerError,
|
||||||
|
"Internal Error", "Internal Error")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate checks whether a user is authenticated
|
// getAuthenticatedSession checks whether a user is authenticated and returns a session object and nil error if so
|
||||||
func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int {
|
// Returns nil, ErrNeedsLogin if user needs to login.
|
||||||
|
// Set-Cookie headers may be set on the response as a side-effect of calling this method.
|
||||||
|
func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.Request) (*sessionsapi.SessionState, error) {
|
||||||
var saveSession, clearSession, revalidated bool
|
var saveSession, clearSession, revalidated bool
|
||||||
remoteAddr := getRemoteAddr(req)
|
remoteAddr := getRemoteAddr(req)
|
||||||
|
|
||||||
@ -720,7 +742,7 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int
|
|||||||
err = p.SaveSession(rw, req, session)
|
err = p.SaveSession(rw, req, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.PrintAuthf(session.Email, req, logger.AuthError, "Save session error %s", err)
|
logger.PrintAuthf(session.Email, req, logger.AuthError, "Save session error %s", err)
|
||||||
return http.StatusInternalServerError
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,15 +758,14 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int
|
|||||||
}
|
}
|
||||||
|
|
||||||
if session == nil {
|
if session == nil {
|
||||||
// Check if is an ajax request and return unauthorized to avoid a redirect
|
return nil, ErrNeedsLogin
|
||||||
// to the login page
|
|
||||||
if p.isAjax(req) {
|
|
||||||
return http.StatusUnauthorized
|
|
||||||
}
|
|
||||||
return http.StatusForbidden
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, the user is authenticated. proxy normally
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addHeadersForProxying adds the appropriate headers the request / response for proxying
|
||||||
|
func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Request, session *sessionsapi.SessionState) {
|
||||||
if p.PassBasicAuth {
|
if p.PassBasicAuth {
|
||||||
req.SetBasicAuth(session.User, p.BasicAuthPassword)
|
req.SetBasicAuth(session.User, p.BasicAuthPassword)
|
||||||
req.Header["X-Forwarded-User"] = []string{session.User}
|
req.Header["X-Forwarded-User"] = []string{session.User}
|
||||||
@ -781,7 +802,6 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int
|
|||||||
} else {
|
} else {
|
||||||
rw.Header().Set("GAP-Auth", session.Email)
|
rw.Header().Set("GAP-Auth", session.Email)
|
||||||
}
|
}
|
||||||
return http.StatusAccepted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckBasicAuth checks the requests Authorization header for basic auth
|
// CheckBasicAuth checks the requests Authorization header for basic auth
|
||||||
@ -815,7 +835,7 @@ func (p *OAuthProxy) CheckBasicAuth(req *http.Request) (*sessionsapi.SessionStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isAjax checks if a request is an ajax request
|
// isAjax checks if a request is an ajax request
|
||||||
func (p *OAuthProxy) isAjax(req *http.Request) bool {
|
func isAjax(req *http.Request) bool {
|
||||||
acceptValues, ok := req.Header["accept"]
|
acceptValues, ok := req.Header["accept"]
|
||||||
if !ok {
|
if !ok {
|
||||||
acceptValues = req.Header["Accept"]
|
acceptValues = req.Header["Accept"]
|
||||||
|
Loading…
Reference in New Issue
Block a user