mirror of https://github.com/vouch/vouch-proxy
135 lines
3.6 KiB
Go
135 lines
3.6 KiB
Go
/*
|
|
|
|
Copyright 2020 The Vouch Proxy Authors.
|
|
Use of this source code is governed by The MIT License (MIT) that
|
|
can be found in the LICENSE file. Software distributed under The
|
|
MIT License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
|
|
OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
*/
|
|
|
|
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/vouch/vouch-proxy/pkg/cfg"
|
|
"github.com/vouch/vouch-proxy/pkg/jwtmanager"
|
|
"github.com/vouch/vouch-proxy/pkg/responses"
|
|
)
|
|
|
|
var (
|
|
errNoJWT = errors.New("no jwt found in request")
|
|
errNoUser = errors.New("no User found in jwt")
|
|
)
|
|
|
|
// ValidateRequestHandler /validate
|
|
func ValidateRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|
fastlog.Debug("/validate")
|
|
|
|
jwt := jwtmanager.FindJWT(r)
|
|
if jwt == "" {
|
|
send401or200PublicAccess(w, r, errNoJWT)
|
|
return
|
|
}
|
|
|
|
claims, err := jwtmanager.ClaimsFromJWT(jwt)
|
|
if err != nil {
|
|
send401or200PublicAccess(w, r, err)
|
|
return
|
|
}
|
|
|
|
if claims.Username == "" {
|
|
send401or200PublicAccess(w, r, errNoUser)
|
|
return
|
|
}
|
|
|
|
if !cfg.Cfg.AllowAllUsers {
|
|
if !claims.SiteInAudience(r.Host) {
|
|
send401or200PublicAccess(w, r,
|
|
fmt.Errorf("http header 'Host: %s' not authorized for configured `vouch.domains` (is Host being sent properly?)", r.Host))
|
|
return
|
|
}
|
|
}
|
|
|
|
generateCustomClaimsHeaders(w, claims)
|
|
w.Header().Add(cfg.Cfg.Headers.User, claims.Username)
|
|
w.Header().Add(cfg.Cfg.Headers.Success, "true")
|
|
|
|
if cfg.Cfg.Headers.AccessToken != "" && claims.PAccessToken != "" {
|
|
w.Header().Add(cfg.Cfg.Headers.AccessToken, claims.PAccessToken)
|
|
}
|
|
if cfg.Cfg.Headers.IDToken != "" && claims.PIdToken != "" {
|
|
w.Header().Add(cfg.Cfg.Headers.IDToken, claims.PIdToken)
|
|
}
|
|
// fastlog.Debugf("response headers %+v", w.Header())
|
|
// fastlog.Debug("response header",
|
|
// zap.String(cfg.Cfg.Headers.User, w.Header().Get(cfg.Cfg.Headers.User)))
|
|
fastlog.Debug("response header",
|
|
zap.Any("all headers", w.Header()))
|
|
|
|
// good to go!!
|
|
|
|
if cfg.Cfg.Testing {
|
|
responses.RenderIndex(w, "user authorized "+claims.Username)
|
|
} else {
|
|
responses.OK200(w, r)
|
|
}
|
|
|
|
// TODO
|
|
// parse the jwt and see if the claim is valid for the domain
|
|
|
|
}
|
|
|
|
func generateCustomClaimsHeaders(w http.ResponseWriter, claims *jwtmanager.VouchClaims) {
|
|
if len(cfg.Cfg.Headers.ClaimsCleaned) > 0 {
|
|
log.Debug("Found claims in config, finding specific keys...")
|
|
// Run through all the claims found
|
|
for k, v := range claims.CustomClaims {
|
|
// Run through the claims we are looking for
|
|
for claim, header := range cfg.Cfg.Headers.ClaimsCleaned {
|
|
// Check for matching claim
|
|
if claim == k {
|
|
log.Debugf("Found matching claim key: %s", k)
|
|
if val, ok := v.([]interface{}); ok {
|
|
strs := make([]string, len(val))
|
|
for i, v := range val {
|
|
strs[i] = fmt.Sprintf("\"%s\"", v)
|
|
}
|
|
log.Debugf("Adding header for claim %s - %s: %s", k, header, val)
|
|
w.Header().Add(header, strings.Join(strs, ","))
|
|
} else {
|
|
// convert to string
|
|
val := fmt.Sprint(v)
|
|
if reflect.TypeOf(val).Kind() == reflect.String {
|
|
// if val, ok := v.(string); ok {
|
|
w.Header().Add(header, val)
|
|
log.Debugf("Adding header for claim %s - %s: %s", k, header, val)
|
|
} else {
|
|
log.Errorf("Couldn't parse header type for %s %+v. Please submit an issue.", k, v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func send401or200PublicAccess(w http.ResponseWriter, r *http.Request, e error) {
|
|
if cfg.Cfg.PublicAccess {
|
|
log.Debugf("error: %s, but public access is '%v', returning OK200", e, cfg.Cfg.PublicAccess)
|
|
w.Header().Add(cfg.Cfg.Headers.User, "")
|
|
responses.OK200(w, r)
|
|
return
|
|
}
|
|
|
|
responses.Error401(w, r, e)
|
|
}
|