Skip to content

Commit 4db568d

Browse files
committed
Merge branch 'master' into pr/rhansen/310
2 parents 083f1fb + f926918 commit 4db568d

10 files changed

Lines changed: 125 additions & 100 deletions

File tree

.github/workflows/coverage.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: coverage
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
pull_request:
7+
8+
jobs:
9+
coverage:
10+
env:
11+
GOPATH: ${{ github.workspace }}
12+
VOUCH_ROOT: ${{ github.workspace }}/src/github.com/${{ github.repository }}
13+
defaults:
14+
run:
15+
working-directory: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}
16+
runs-on: ubuntu-latest
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
go: ['1.14', '1.15']
21+
# go: ['1.15']
22+
23+
steps:
24+
- uses: actions/setup-go@v2
25+
with:
26+
go-version: ${{ matrix.go }}
27+
- name: checkout
28+
uses: actions/checkout@v2
29+
with:
30+
path: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}
31+
- name: goget
32+
run: ./do.sh goget
33+
- name: coverage test
34+
run: ./do.sh coverage
35+
36+
- name: Send coverage
37+
uses: shogo82148/actions-goveralls@v1
38+
with:
39+
path-to-profile: ${{ env.GOPATH }}/src/github.com/${{ github.repository }}/.cover/cover.out
40+
flag-name: Go-${{ matrix.go }}
41+
parallel: true
42+
43+
# notifies that all test jobs are finished.
44+
finish:
45+
needs: coverage
46+
runs-on: ubuntu-latest
47+
steps:
48+
- uses: shogo82148/actions-goveralls@v1
49+
with:
50+
parallel-finished: true

coverage_report.sh

Lines changed: 0 additions & 52 deletions
This file was deleted.

do.sh

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,11 @@ _redact() {
148148
}
149149

150150
coverage() {
151-
export EXTRA_TEST_ARGS='-cover'
152-
test
153-
go tool cover -html=coverage.out -o coverage.html
151+
mkdir -p .cover && go test -v -coverprofile=.cover/cover.out ./...
152+
}
153+
154+
coveragereport() {
155+
go tool cover -html=.cover/cover.out -o .cover/coverage.html
154156
}
155157

156158
test() {
@@ -347,7 +349,8 @@ usage() {
347349
$0 drunalpine [args] - run docker container for alpine
348350
$0 test [./pkg_test.go] - run go tests (defaults to all tests)
349351
$0 test_logging - test the logging output
350-
$0 coverage - coverage report
352+
$0 coverage - coverage test
353+
$0 coveragereport - coverage report published to .cover/coverage.html
351354
$0 profile - go pprof tools
352355
$0 bug_report domain.com - print config file removing secrets and each provided domain
353356
$0 gogo [gocmd] - run, build, any go cmd
@@ -379,6 +382,7 @@ case "$ARG" in
379382
|'watch' \
380383
|'gobuildstatic' \
381384
|'coverage' \
385+
|'coveragereport' \
382386
|'stats' \
383387
|'usage' \
384388
|'bug_report' \

handlers/auth.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,13 @@ func AuthStateHandler(w http.ResponseWriter, r *http.Request) {
9797
// SUCCESS!! they are authorized
9898

9999
// issue the jwt
100-
tokenstring := jwtmanager.CreateUserTokenString(user, customClaims, ptokens)
100+
101+
tokenstring, err := jwtmanager.NewVPJWT(user, customClaims, ptokens)
102+
if err != nil {
103+
responses.Error500(w, r, fmt.Errorf("/auth Token creation failure: %w . Please seek support from your administrator", err))
104+
return
105+
106+
}
101107
cookie.SetCookie(w, r, tokenstring)
102108

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

164170
// nothing configured, allow everyone through
165171
default:

handlers/handlers_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@ func init() {
179179
lc = jwtmanager.VouchClaims{
180180
u1.Sub,
181181
u1.Username,
182-
jwtmanager.Sites,
183182
customClaims.Claims,
184183
t1.PAccessToken,
185184
t1.PIdToken,
@@ -201,7 +200,8 @@ func TestParsedIdPTokens(t *testing.T) {
201200
for _, tt := range tests {
202201
t.Run(tt.name, func(t *testing.T) {
203202
setUp(tt.configFile)
204-
uts := jwtmanager.CreateUserTokenString(u1, customClaims, t1)
203+
uts, err := jwtmanager.NewVPJWT(u1, customClaims, t1)
204+
assert.NoError(t, err)
205205
utsParsed, _ := jwtmanager.ParseTokenString(uts)
206206
utsPtokens, _ := jwtmanager.PTokenClaims(utsParsed)
207207

handlers/validate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func ValidateRequestHandler(w http.ResponseWriter, r *http.Request) {
5151
}
5252

5353
if !cfg.Cfg.AllowAllUsers {
54-
if !claims.SiteInClaims(r.Host) {
54+
if !claims.SiteInAudience(r.Host) {
5555
send401or200PublicAccess(w, r,
5656
fmt.Errorf("http header 'Host: %s' not authorized for configured `vouch.domains` (is Host being sent properly?)", r.Host))
5757
return

handlers/validate_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ func BenchmarkValidateRequestHandler(b *testing.B) {
3737
tokens := structs.PTokens{}
3838
customClaims := structs.CustomClaims{}
3939

40-
userTokenString := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
40+
userTokenString, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
41+
assert.NoError(b, err)
4142

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

83-
userTokenString := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
84+
vpjwt, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
85+
assert.NoError(t, err)
8486

8587
c := &http.Cookie{
8688
// Name: cfg.Cfg.Cookie.Name + "_1of1",
8789
Name: cfg.Cfg.Cookie.Name,
88-
Value: userTokenString,
90+
Value: vpjwt,
8991
Expires: time.Now().Add(1 * time.Hour),
9092
}
9193

@@ -169,7 +171,8 @@ func TestValidateRequestHandlerWithGroupClaims(t *testing.T) {
169171
Email: "test@example.com",
170172
Name: "Test Name",
171173
}
172-
userTokenString := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
174+
vpjwt, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
175+
assert.NoError(t, err)
173176

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

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

232-
jwt := jwtmanager.CreateUserTokenString(*user, customClaims, tokens)
235+
jwt, err := jwtmanager.NewVPJWT(*user, customClaims, tokens)
236+
assert.NoError(t, err)
233237
badjwt := strings.ReplaceAll(jwt, "a", "z")
234238
badjwt = strings.ReplaceAll(badjwt, "b", "x")
235239

pkg/jwtmanager/jwtmanager.go

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,12 @@ import (
2929
"github.com/vouch/vouch-proxy/pkg/structs"
3030
)
3131

32-
// const numSites = 2
32+
const comma = ","
3333

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

48-
// CustomClaims implementation
49-
// var CustomClaims map[string]interface{}
50-
51-
// Sites added to VouchClaims
52-
var Sites []string
5347
var logger *zap.Logger
5448
var log *zap.SugaredLogger
49+
var aud string
5550

5651
// Configure see main.go configure()
5752
func Configure() {
5853
log = cfg.Logging.Logger
5954
logger = cfg.Logging.FastLogger
6055
cacheConfigure()
56+
aud = audience()
6157
StandardClaims = jwt.StandardClaims{
62-
Issuer: cfg.Cfg.JWT.Issuer,
58+
Issuer: cfg.Cfg.JWT.Issuer,
59+
Audience: aud,
6360
}
64-
populateSites()
6561
}
6662

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

77-
// CreateUserTokenString converts user to signed jwt
78-
func CreateUserTokenString(u structs.User, customClaims structs.CustomClaims, ptokens structs.PTokens) string {
78+
// NewVPJWT issue a signed Vouch Proxy JWT for a user
79+
func NewVPJWT(u structs.User, customClaims structs.CustomClaims, ptokens structs.PTokens) (string, error) {
7980
// User`token`
8081
// u.PrepareUserData()
8182
claims := VouchClaims{
8283
u.Sub,
8384
u.Username,
84-
Sites,
8585
customClaims.Claims,
8686
ptokens.PAccessToken,
8787
ptokens.PIdToken,
8888
StandardClaims,
8989
}
9090

91+
claims.Audience = aud
92+
claims.ExpiresAt = time.Now().Add(time.Minute * time.Duration(cfg.Cfg.JWT.MaxAge)).Unix()
93+
9194
// https://github.com/vouch/vouch-proxy/issues/287
9295
if cfg.Cfg.Headers.AccessToken == "" {
9396
claims.PAccessToken = ""
@@ -97,8 +100,6 @@ func CreateUserTokenString(u structs.User, customClaims structs.CustomClaims, pt
97100
claims.PIdToken = ""
98101
}
99102

100-
claims.StandardClaims.ExpiresAt = time.Now().Add(time.Minute * time.Duration(cfg.Cfg.JWT.MaxAge)).Unix()
101-
102103
// https://godoc.org/github.com/dgrijalva/jwt-go#NewWithClaims
103104
token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), claims)
104105

@@ -109,15 +110,15 @@ func CreateUserTokenString(u structs.User, customClaims structs.CustomClaims, pt
109110
ss, err := token.SignedString([]byte(cfg.Cfg.JWT.Secret))
110111
// ss, err := token.SignedString([]byte("testing"))
111112
if ss == "" || err != nil {
112-
log.Errorf("signed token error: %s", err)
113+
return "", fmt.Errorf("New JWT: signed token error: %s", err)
113114
}
114115
if cfg.Cfg.JWT.Compress {
115116
ss, err = compressAndEncodeTokenString(ss)
116117
if ss == "" || err != nil {
117-
log.Errorf("compressed token error: %s", err)
118+
return "", fmt.Errorf("New JWT: compressed token error: %w", err)
118119
}
119120
}
120-
return ss
121+
return ss, nil
121122
}
122123

123124
// TokenIsValid gett better error reporting
@@ -143,11 +144,11 @@ func TokenIsValid(token *jwt.Token, err error) bool {
143144
func SiteInToken(site string, token *jwt.Token) bool {
144145
if claims, ok := token.Claims.(*VouchClaims); ok {
145146
log.Debugf("site %s claim %v", site, claims)
146-
if claims.SiteInClaims(site) {
147+
if claims.SiteInAudience(site) {
147148
return true
148149
}
149150
}
150-
log.Errorf("site %s not found in token", site)
151+
log.Errorf("site %s not found in token audience", site)
151152
return false
152153
}
153154

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

171172
}
172173

173-
// SiteInClaims does the claim contain the value?
174-
func (claims *VouchClaims) SiteInClaims(site string) bool {
175-
for _, s := range claims.Sites {
174+
// SiteInAudience does the claim contain the value?
175+
func (claims *VouchClaims) SiteInAudience(site string) bool {
176+
for _, s := range strings.Split(aud, comma) {
176177
if strings.Contains(site, s) {
177-
log.Debugf("site %s is found for claims.Site %s", site, s)
178+
log.Debugf("site %s is found for claims.Audience %s", site, s)
178179
return true
179180
}
180181
}

0 commit comments

Comments
 (0)