Honor DurationSeconds properly for WebIdentity (#8581)

Also cleanup code to add various constants for
verbatim strings across the code base.

Fixes #8482
This commit is contained in:
Harshavardhana
2019-11-29 05:27:54 -08:00
committed by Nitish Tiwari
parent c7844fb1fb
commit b21835f195
10 changed files with 194 additions and 95 deletions

View File

@@ -28,6 +28,7 @@ import (
jwtgo "github.com/dgrijalva/jwt-go"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/env"
xnet "github.com/minio/minio/pkg/net"
)
@@ -107,36 +108,19 @@ type JWT struct {
Config
}
func expToInt64(expI interface{}) (expAt int64, err error) {
switch exp := expI.(type) {
case float64:
expAt = int64(exp)
case int64:
expAt = exp
case json.Number:
expAt, err = exp.Int64()
if err != nil {
return 0, err
}
default:
return 0, ErrInvalidDuration
}
return expAt, nil
}
// GetDefaultExpiration - returns the expiration seconds expected.
func GetDefaultExpiration(dsecs string) (time.Duration, error) {
defaultExpiryDuration := time.Duration(60) * time.Minute // Defaults to 1hr.
if dsecs != "" {
expirySecs, err := strconv.ParseInt(dsecs, 10, 64)
if err != nil {
return 0, ErrInvalidDuration
return 0, auth.ErrInvalidDuration
}
// The duration, in seconds, of the role session.
// The value can range from 900 seconds (15 minutes)
// to 12 hours.
if expirySecs < 900 || expirySecs > 43200 {
return 0, ErrInvalidDuration
return 0, auth.ErrInvalidDuration
}
defaultExpiryDuration = time.Duration(expirySecs) * time.Second
@@ -144,6 +128,39 @@ func GetDefaultExpiration(dsecs string) (time.Duration, error) {
return defaultExpiryDuration, nil
}
func updateClaimsExpiry(dsecs string, claims map[string]interface{}) error {
expStr := claims["exp"]
if expStr == "" {
return ErrTokenExpired
}
// No custom duration requested, the claims can be used as is.
if dsecs == "" {
return nil
}
expAt, err := auth.ExpToInt64(expStr)
if err != nil {
return err
}
defaultExpiryDuration, err := GetDefaultExpiration(dsecs)
if err != nil {
return err
}
// Verify if JWT expiry is lesser than default expiry duration,
// if that is the case then set the default expiration to be
// from the JWT expiry claim.
if time.Unix(expAt, 0).UTC().Sub(time.Now().UTC()) < defaultExpiryDuration {
defaultExpiryDuration = time.Unix(expAt, 0).UTC().Sub(time.Now().UTC())
} // else honor the specified expiry duration.
expiry := time.Now().UTC().Add(defaultExpiryDuration).Unix()
claims["exp"] = strconv.FormatInt(expiry, 10) // update with new expiry.
return nil
}
// Validate - validates the access token.
func (p *JWT) Validate(token, dsecs string) (map[string]interface{}, error) {
jp := new(jwtgo.Parser)
@@ -173,25 +190,10 @@ func (p *JWT) Validate(token, dsecs string) (map[string]interface{}, error) {
return nil, ErrTokenExpired
}
expAt, err := expToInt64(claims["exp"])
if err != nil {
if err = updateClaimsExpiry(dsecs, claims); err != nil {
return nil, err
}
defaultExpiryDuration, err := GetDefaultExpiration(dsecs)
if err != nil {
return nil, err
}
if time.Unix(expAt, 0).UTC().Sub(time.Now().UTC()) < defaultExpiryDuration {
defaultExpiryDuration = time.Unix(expAt, 0).UTC().Sub(time.Now().UTC())
}
expiry := time.Now().UTC().Add(defaultExpiryDuration).Unix()
if expAt < expiry {
claims["exp"] = strconv.FormatInt(expAt, 64)
}
return claims, nil
}

View File

@@ -1,5 +1,5 @@
/*
* MinIO Cloud Storage, (C) 2018 MinIO, Inc.
* MinIO Cloud Storage, (C) 2018-2019 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,42 @@ import (
xnet "github.com/minio/minio/pkg/net"
)
func TestUpdateClaimsExpiry(t *testing.T) {
testCases := []struct {
exp interface{}
dsecs string
expectedFailure bool
}{
{"", "", true},
{"-1", "0", true},
{"-1", "900", true},
{"1574812326", "900", false},
{1574812326, "900", false},
{int64(1574812326), "900", false},
{int(1574812326), "900", false},
{uint(1574812326), "900", false},
{uint64(1574812326), "900", false},
{json.Number("1574812326"), "900", false},
{1574812326.000, "900", false},
{time.Duration(3) * time.Minute, "900", false},
}
for _, testCase := range testCases {
testCase := testCase
t.Run("", func(t *testing.T) {
claims := map[string]interface{}{}
claims["exp"] = testCase.exp
err := updateClaimsExpiry(testCase.dsecs, claims)
if err != nil && !testCase.expectedFailure {
t.Errorf("Expected success, got failure %s", err)
}
if err == nil && testCase.expectedFailure {
t.Error("Expected failure, got success")
}
})
}
}
func TestJWT(t *testing.T) {
const jsonkey = `{"keys":
[

View File

@@ -38,8 +38,7 @@ type Validator interface {
// ErrTokenExpired - error token expired
var (
ErrTokenExpired = errors.New("token expired")
ErrInvalidDuration = errors.New("duration higher than token expiry")
ErrTokenExpired = errors.New("token expired")
)
// Validators - holds list of providers indexed by provider id.