Skip to content

Commit

Permalink
Merge branch 'master' into pr/rhansen/310
Browse files Browse the repository at this point in the history
  • Loading branch information
bnfinet committed Feb 2, 2021
2 parents 083f1fb + f926918 commit 4db568d
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 100 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: coverage

on:
workflow_dispatch:
push:
pull_request:

jobs:
coverage:
env:
GOPATH: ${{ github.workspace }}
VOUCH_ROOT: ${{ github.workspace }}/src/github.com/${{ github.repository }}
defaults:
run:
working-directory: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
go: ['1.14', '1.15']
# go: ['1.15']

steps:
- uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
- name: checkout
uses: actions/checkout@v2
with:
path: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}
- name: goget
run: ./do.sh goget
- name: coverage test
run: ./do.sh coverage

- name: Send coverage
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}/.cover/cover.out
flag-name: Go-${{ matrix.go }}
parallel: true

# notifies that all test jobs are finished.
finish:
needs: coverage
runs-on: ubuntu-latest
steps:
- uses: shogo82148/actions-goveralls@v1
with:
parallel-finished: true
52 changes: 0 additions & 52 deletions coverage_report.sh

This file was deleted.

12 changes: 8 additions & 4 deletions do.sh
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ _redact() {
}

coverage() {
export EXTRA_TEST_ARGS='-cover'
test
go tool cover -html=coverage.out -o coverage.html
mkdir -p .cover && go test -v -coverprofile=.cover/cover.out ./...
}

coveragereport() {
go tool cover -html=.cover/cover.out -o .cover/coverage.html
}

test() {
Expand Down Expand Up @@ -347,7 +349,8 @@ usage() {
$0 drunalpine [args] - run docker container for alpine
$0 test [./pkg_test.go] - run go tests (defaults to all tests)
$0 test_logging - test the logging output
$0 coverage - coverage report
$0 coverage - coverage test
$0 coveragereport - coverage report published to .cover/coverage.html
$0 profile - go pprof tools
$0 bug_report domain.com - print config file removing secrets and each provided domain
$0 gogo [gocmd] - run, build, any go cmd
Expand Down Expand Up @@ -379,6 +382,7 @@ case "$ARG" in
|'watch' \
|'gobuildstatic' \
|'coverage' \
|'coveragereport' \
|'stats' \
|'usage' \
|'bug_report' \
Expand Down
12 changes: 9 additions & 3 deletions handlers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ func AuthStateHandler(w http.ResponseWriter, r *http.Request) {
// SUCCESS!! they are authorized

// issue the jwt
tokenstring := jwtmanager.CreateUserTokenString(user, customClaims, ptokens)

tokenstring, err := jwtmanager.NewVPJWT(user, customClaims, ptokens)
if err != nil {
responses.Error500(w, r, fmt.Errorf("/auth Token creation failure: %w . Please seek support from your administrator", err))
return

}
cookie.SetCookie(w, r, tokenstring)

// get the originally requested URL so we can send them on their way
Expand Down Expand Up @@ -156,10 +162,10 @@ func verifyUser(u interface{}) (bool, error) {
// Domains
case len(cfg.Cfg.Domains) != 0:
if domains.IsUnderManagement(user.Email) {
log.Debugf("verifyUser: Success! Email %s found within a "+cfg.Branding.FullName+" managed domain", user.Email)
log.Debugf("verifyUser: Success! Email %s found within a %s managed domain", user.Email, cfg.Branding.FullName)
return true, nil
}
return false, fmt.Errorf("verifyUser: Email %s is not within a "+cfg.Branding.FullName+" managed domain", user.Email)
return false, fmt.Errorf("verifyUser: Email %s is not within a %s managed domain", user.Email, cfg.Branding.FullName)

// nothing configured, allow everyone through
default:
Expand Down
4 changes: 2 additions & 2 deletions handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ func init() {
lc = jwtmanager.VouchClaims{
u1.Sub,
u1.Username,
jwtmanager.Sites,
customClaims.Claims,
t1.PAccessToken,
t1.PIdToken,
Expand All @@ -201,7 +200,8 @@ func TestParsedIdPTokens(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
setUp(tt.configFile)
uts := jwtmanager.CreateUserTokenString(u1, customClaims, t1)
uts, err := jwtmanager.NewVPJWT(u1, customClaims, t1)
assert.NoError(t, err)
utsParsed, _ := jwtmanager.ParseTokenString(uts)
utsPtokens, _ := jwtmanager.PTokenClaims(utsParsed)

Expand Down
2 changes: 1 addition & 1 deletion handlers/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func ValidateRequestHandler(w http.ResponseWriter, r *http.Request) {
}

if !cfg.Cfg.AllowAllUsers {
if !claims.SiteInClaims(r.Host) {
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
Expand Down
16 changes: 10 additions & 6 deletions handlers/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ func BenchmarkValidateRequestHandler(b *testing.B) {
tokens := structs.PTokens{}
customClaims := structs.CustomClaims{}

userTokenString := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
userTokenString, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
assert.NoError(b, err)

c := &http.Cookie{
// Name: cfg.Cfg.Cookie.Name + "_1of1",
Expand Down Expand Up @@ -80,12 +81,13 @@ func TestValidateRequestHandlerPerf(t *testing.T) {
tokens := structs.PTokens{}
customClaims := structs.CustomClaims{}

userTokenString := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
vpjwt, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
assert.NoError(t, err)

c := &http.Cookie{
// Name: cfg.Cfg.Cookie.Name + "_1of1",
Name: cfg.Cfg.Cookie.Name,
Value: userTokenString,
Value: vpjwt,
Expires: time.Now().Add(1 * time.Hour),
}

Expand Down Expand Up @@ -169,7 +171,8 @@ func TestValidateRequestHandlerWithGroupClaims(t *testing.T) {
Email: "[email protected]",
Name: "Test Name",
}
userTokenString := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
vpjwt, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
assert.NoError(t, err)

req, err := http.NewRequest("GET", "/validate", nil)
if err != nil {
Expand All @@ -179,7 +182,7 @@ func TestValidateRequestHandlerWithGroupClaims(t *testing.T) {
req.AddCookie(&http.Cookie{
// Name: cfg.Cfg.Cookie.Name + "_1of1",
Name: cfg.Cfg.Cookie.Name,
Value: userTokenString,
Value: vpjwt,
Expires: time.Now().Add(1 * time.Hour),
})

Expand Down Expand Up @@ -229,7 +232,8 @@ func TestJWTCacheHandler(t *testing.T) {
tokens := structs.PTokens{}
customClaims := structs.CustomClaims{}

jwt := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
jwt, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
assert.NoError(t, err)
badjwt := strings.ReplaceAll(jwt, "a", "z")
badjwt = strings.ReplaceAll(badjwt, "b", "x")

Expand Down
57 changes: 29 additions & 28 deletions pkg/jwtmanager/jwtmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,12 @@ import (
"github.com/vouch/vouch-proxy/pkg/structs"
)

// const numSites = 2
const comma = ","

// VouchClaims jwt Claims specific to vouch
type VouchClaims struct {
Sub string `json:"sub"`
Username string `json:"username"`
Sites []string `json:"sites"` // tempting to make this a map but the array is fewer characters in the jwt
Sub string `json:"sub"`
Username string `json:"username"`
CustomClaims map[string]interface{}
PAccessToken string
PIdToken string
Expand All @@ -45,49 +44,53 @@ type VouchClaims struct {
// StandardClaims jwt.StandardClaims implementation
var StandardClaims jwt.StandardClaims

// CustomClaims implementation
// var CustomClaims map[string]interface{}

// Sites added to VouchClaims
var Sites []string
var logger *zap.Logger
var log *zap.SugaredLogger
var aud string

// Configure see main.go configure()
func Configure() {
log = cfg.Logging.Logger
logger = cfg.Logging.FastLogger
cacheConfigure()
aud = audience()
StandardClaims = jwt.StandardClaims{
Issuer: cfg.Cfg.JWT.Issuer,
Issuer: cfg.Cfg.JWT.Issuer,
Audience: aud,
}
populateSites()
}

func populateSites() {
Sites = make([]string, 0)
// `aud` of the issued JWT https://tools.ietf.org/html/rfc7519#section-4.1.3
func audience() string {
aud := make([]string, 0)
// TODO: the Sites that end up in the JWT come from here
// if we add fine grain ability (ACL?) to the equation
// then we're going to have to add something fancier here
for i := 0; i < len(cfg.Cfg.Domains); i++ {
Sites = append(Sites, cfg.Cfg.Domains[i])
aud = append(aud, cfg.Cfg.Domains[i])
}
if cfg.Cfg.Cookie.Domain != "" {
aud = append(aud, cfg.Cfg.Cookie.Domain)
}
return strings.Join(aud, comma)
}

// CreateUserTokenString converts user to signed jwt
func CreateUserTokenString(u structs.User, customClaims structs.CustomClaims, ptokens structs.PTokens) string {
// NewVPJWT issue a signed Vouch Proxy JWT for a user
func NewVPJWT(u structs.User, customClaims structs.CustomClaims, ptokens structs.PTokens) (string, error) {
// User`token`
// u.PrepareUserData()
claims := VouchClaims{
u.Sub,
u.Username,
Sites,
customClaims.Claims,
ptokens.PAccessToken,
ptokens.PIdToken,
StandardClaims,
}

claims.Audience = aud
claims.ExpiresAt = time.Now().Add(time.Minute * time.Duration(cfg.Cfg.JWT.MaxAge)).Unix()

// https://github.com/vouch/vouch-proxy/issues/287
if cfg.Cfg.Headers.AccessToken == "" {
claims.PAccessToken = ""
Expand All @@ -97,8 +100,6 @@ func CreateUserTokenString(u structs.User, customClaims structs.CustomClaims, pt
claims.PIdToken = ""
}

claims.StandardClaims.ExpiresAt = time.Now().Add(time.Minute * time.Duration(cfg.Cfg.JWT.MaxAge)).Unix()

// https://godoc.org/github.com/dgrijalva/jwt-go#NewWithClaims
token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), claims)

Expand All @@ -109,15 +110,15 @@ func CreateUserTokenString(u structs.User, customClaims structs.CustomClaims, pt
ss, err := token.SignedString([]byte(cfg.Cfg.JWT.Secret))
// ss, err := token.SignedString([]byte("testing"))
if ss == "" || err != nil {
log.Errorf("signed token error: %s", err)
return "", fmt.Errorf("New JWT: signed token error: %s", err)
}
if cfg.Cfg.JWT.Compress {
ss, err = compressAndEncodeTokenString(ss)
if ss == "" || err != nil {
log.Errorf("compressed token error: %s", err)
return "", fmt.Errorf("New JWT: compressed token error: %w", err)
}
}
return ss
return ss, nil
}

// TokenIsValid gett better error reporting
Expand All @@ -143,11 +144,11 @@ func TokenIsValid(token *jwt.Token, err error) bool {
func SiteInToken(site string, token *jwt.Token) bool {
if claims, ok := token.Claims.(*VouchClaims); ok {
log.Debugf("site %s claim %v", site, claims)
if claims.SiteInClaims(site) {
if claims.SiteInAudience(site) {
return true
}
}
log.Errorf("site %s not found in token", site)
log.Errorf("site %s not found in token audience", site)
return false
}

Expand All @@ -170,11 +171,11 @@ func ParseTokenString(tokenString string) (*jwt.Token, error) {

}

// SiteInClaims does the claim contain the value?
func (claims *VouchClaims) SiteInClaims(site string) bool {
for _, s := range claims.Sites {
// SiteInAudience does the claim contain the value?
func (claims *VouchClaims) SiteInAudience(site string) bool {
for _, s := range strings.Split(aud, comma) {
if strings.Contains(site, s) {
log.Debugf("site %s is found for claims.Site %s", site, s)
log.Debugf("site %s is found for claims.Audience %s", site, s)
return true
}
}
Expand Down
Loading

0 comments on commit 4db568d

Please sign in to comment.