mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
jwt: Simplify JWT parsing (#8802)
JWT parsing is simplified by using a custom claim data structure such as MapClaims{}, also writes a custom Unmarshaller for faster unmarshalling. - Avoid as much reflections as possible - Provide the right types for functions as much as possible - Avoid strings.Join, strings.Split to reduce allocations, rely on indexes directly.
This commit is contained in:
parent
9990464cd5
commit
bfe8a9bccc
@ -28,8 +28,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
xjwt "github.com/minio/minio/cmd/jwt"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
@ -179,12 +179,14 @@ func mustGetClaimsFromToken(r *http.Request) map[string]interface{} {
|
||||
|
||||
// Fetch claims in the security token returned by the client.
|
||||
func getClaimsFromToken(r *http.Request) (map[string]interface{}, error) {
|
||||
claims := make(map[string]interface{})
|
||||
claims := xjwt.NewMapClaims()
|
||||
|
||||
token := getSessionToken(r)
|
||||
if token == "" {
|
||||
return claims, nil
|
||||
return claims.Map(), nil
|
||||
}
|
||||
stsTokenCallback := func(jwtToken *jwtgo.Token) (interface{}, error) {
|
||||
|
||||
stsTokenCallback := func(claims *xjwt.MapClaims) ([]byte, error) {
|
||||
// JWT token for x-amz-security-token is signed with admin
|
||||
// secret key, temporary credentials become invalid if
|
||||
// server admin credentials change. This is done to ensure
|
||||
@ -195,66 +197,42 @@ func getClaimsFromToken(r *http.Request) (map[string]interface{}, error) {
|
||||
// on the client side and is treated like an opaque value.
|
||||
return []byte(globalActiveCred.SecretKey), nil
|
||||
}
|
||||
p := &jwtgo.Parser{
|
||||
ValidMethods: []string{
|
||||
jwtgo.SigningMethodHS256.Alg(),
|
||||
jwtgo.SigningMethodHS512.Alg(),
|
||||
},
|
||||
}
|
||||
jtoken, err := p.ParseWithClaims(token, jwtgo.MapClaims(claims), stsTokenCallback)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !jtoken.Valid {
|
||||
|
||||
if err := xjwt.ParseWithClaims(token, claims, stsTokenCallback); err != nil {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
v, ok := claims["accessKey"]
|
||||
if !ok {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
if _, ok = v.(string); !ok {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
|
||||
if globalPolicyOPA == nil {
|
||||
// If OPA is not set and if ldap claim key is set,
|
||||
// allow the claim.
|
||||
if _, ok := claims[ldapUser]; ok {
|
||||
return claims, nil
|
||||
// If OPA is not set and if ldap claim key is set, allow the claim.
|
||||
if _, ok := claims.Lookup(ldapUser); ok {
|
||||
return claims.Map(), nil
|
||||
}
|
||||
|
||||
// If OPA is not set, session token should
|
||||
// have a policy and its mandatory, reject
|
||||
// requests without policy claim.
|
||||
p, pok := claims[iamPolicyClaimName()]
|
||||
_, pok := claims.Lookup(iamPolicyClaimName())
|
||||
if !pok {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
if _, pok = p.(string); !pok {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
sp, spok := claims[iampolicy.SessionPolicyName]
|
||||
// Sub policy is optional, if not set return success.
|
||||
|
||||
sp, spok := claims.Lookup(iampolicy.SessionPolicyName)
|
||||
if !spok {
|
||||
return claims, nil
|
||||
}
|
||||
// Sub policy is set but its not a string, reject such requests
|
||||
spStr, spok := sp.(string)
|
||||
if !spok {
|
||||
return nil, errAuthentication
|
||||
return claims.Map(), nil
|
||||
}
|
||||
// Looks like subpolicy is set and is a string, if set then its
|
||||
// base64 encoded, decode it. Decoding fails reject such requests.
|
||||
spBytes, err := base64.StdEncoding.DecodeString(spStr)
|
||||
spBytes, err := base64.StdEncoding.DecodeString(sp)
|
||||
if err != nil {
|
||||
// Base64 decoding fails, we should log to indicate
|
||||
// something is malforming the request sent by client.
|
||||
logger.LogIf(context.Background(), err, logger.Application)
|
||||
return nil, errAuthentication
|
||||
}
|
||||
claims[iampolicy.SessionPolicyName] = string(spBytes)
|
||||
claims.MapClaims[iampolicy.SessionPolicyName] = string(spBytes)
|
||||
}
|
||||
return claims, nil
|
||||
|
||||
return claims.Map(), nil
|
||||
}
|
||||
|
||||
// Fetch claims in the security token returned by the client and validate the token.
|
||||
|
@ -201,7 +201,9 @@ func getReqAccessCred(r *http.Request, region string) (cred auth.Credentials) {
|
||||
if owner {
|
||||
return globalActiveCred
|
||||
}
|
||||
cred, _ = globalIAMSys.GetUser(claims.AccessKey())
|
||||
if claims != nil {
|
||||
cred, _ = globalIAMSys.GetUser(claims.AccessKey)
|
||||
}
|
||||
}
|
||||
return cred
|
||||
}
|
||||
|
189
cmd/jwt.go
189
cmd/jwt.go
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc.
|
||||
* MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,12 +19,12 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
jwtreq "github.com/dgrijalva/jwt-go/request"
|
||||
xjwt "github.com/minio/minio/cmd/jwt"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
)
|
||||
@ -35,8 +35,8 @@ const (
|
||||
// Default JWT token for web handlers is one day.
|
||||
defaultJWTExpiry = 24 * time.Hour
|
||||
|
||||
// Inter-node JWT token expiry is 100 years approx.
|
||||
defaultInterNodeJWTExpiry = 100 * 365 * 24 * time.Hour
|
||||
// Inter-node JWT token expiry is 15 minutes.
|
||||
defaultInterNodeJWTExpiry = 15 * time.Minute
|
||||
|
||||
// URL JWT token expiry is one minute (might be exposed).
|
||||
defaultURLJWTExpiry = time.Minute
|
||||
@ -73,42 +73,23 @@ func authenticateJWTUsersWithCredentials(credentials auth.Credentials, expiresAt
|
||||
return "", errAuthentication
|
||||
}
|
||||
|
||||
claims := jwtgo.MapClaims{}
|
||||
claims["exp"] = expiresAt.Unix()
|
||||
claims["sub"] = credentials.AccessKey
|
||||
claims["accessKey"] = credentials.AccessKey
|
||||
claims := xjwt.NewMapClaims()
|
||||
claims.SetExpiry(expiresAt)
|
||||
claims.SetAccessKey(credentials.AccessKey)
|
||||
|
||||
jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
|
||||
return jwt.SignedString([]byte(serverCred.SecretKey))
|
||||
}
|
||||
|
||||
func authenticateJWTAdmin(accessKey, secretKey string, expiry time.Duration) (string, error) {
|
||||
passedCredential, err := auth.CreateCredentials(accessKey, secretKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
serverCred := globalActiveCred
|
||||
|
||||
if serverCred.AccessKey != passedCredential.AccessKey {
|
||||
return "", errInvalidAccessKeyID
|
||||
}
|
||||
|
||||
if !serverCred.Equal(passedCredential) {
|
||||
return "", errAuthentication
|
||||
}
|
||||
|
||||
claims := jwtgo.MapClaims{}
|
||||
claims["exp"] = UTCNow().Add(expiry).Unix()
|
||||
claims["sub"] = passedCredential.AccessKey
|
||||
claims["accessKey"] = passedCredential.AccessKey
|
||||
func authenticateNode(accessKey, secretKey, audience string) (string, error) {
|
||||
claims := xjwt.NewStandardClaims()
|
||||
claims.SetExpiry(UTCNow().Add(defaultInterNodeJWTExpiry))
|
||||
claims.SetAccessKey(accessKey)
|
||||
claims.SetAudience(audience)
|
||||
claims.SetIssuer(ReleaseTag)
|
||||
|
||||
jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
|
||||
return jwt.SignedString([]byte(serverCred.SecretKey))
|
||||
}
|
||||
|
||||
func authenticateNode(accessKey, secretKey string) (string, error) {
|
||||
return authenticateJWTAdmin(accessKey, secretKey, defaultInterNodeJWTExpiry)
|
||||
return jwt.SignedString([]byte(secretKey))
|
||||
}
|
||||
|
||||
func authenticateWeb(accessKey, secretKey string) (string, error) {
|
||||
@ -120,63 +101,29 @@ func authenticateURL(accessKey, secretKey string) (string, error) {
|
||||
}
|
||||
|
||||
// Callback function used for parsing
|
||||
func webTokenCallback(jwtToken *jwtgo.Token) (interface{}, error) {
|
||||
if _, ok := jwtToken.Method.(*jwtgo.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method: %v", jwtToken.Header["alg"])
|
||||
}
|
||||
|
||||
if err := jwtToken.Claims.Valid(); err != nil {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
|
||||
if claimsPtr, ok := jwtToken.Claims.(*jwtgo.MapClaims); ok {
|
||||
claims := *claimsPtr
|
||||
accessKey, ok := claims["accessKey"].(string)
|
||||
if !ok {
|
||||
accessKey, ok = claims["sub"].(string)
|
||||
if !ok {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
}
|
||||
if accessKey == globalActiveCred.AccessKey {
|
||||
return []byte(globalActiveCred.SecretKey), nil
|
||||
}
|
||||
if globalIAMSys == nil {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
if _, ok = claims["aud"].(string); !ok {
|
||||
cred, ok := globalIAMSys.GetUser(accessKey)
|
||||
if !ok {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
return []byte(cred.SecretKey), nil
|
||||
}
|
||||
func webTokenCallback(claims *xjwt.MapClaims) ([]byte, error) {
|
||||
if claims.AccessKey == globalActiveCred.AccessKey {
|
||||
return []byte(globalActiveCred.SecretKey), nil
|
||||
}
|
||||
|
||||
return nil, errAuthentication
|
||||
}
|
||||
|
||||
func parseJWTWithClaims(tokenString string, claims jwtgo.Claims) (*jwtgo.Token, error) {
|
||||
p := &jwtgo.Parser{
|
||||
SkipClaimsValidation: true,
|
||||
ValidMethods: []string{
|
||||
jwtgo.SigningMethodHS256.Alg(),
|
||||
jwtgo.SigningMethodHS512.Alg(),
|
||||
},
|
||||
if globalIAMSys == nil {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
jwtToken, err := p.ParseWithClaims(tokenString, claims, webTokenCallback)
|
||||
ok, err := globalIAMSys.IsTempUser(claims.AccessKey)
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *jwtgo.ValidationError:
|
||||
if e.Inner == nil {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
return nil, e.Inner
|
||||
if err == errNoSuchUser {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
return nil, errAuthentication
|
||||
return nil, err
|
||||
}
|
||||
return jwtToken, nil
|
||||
if ok {
|
||||
return []byte(globalActiveCred.SecretKey), nil
|
||||
}
|
||||
cred, ok := globalIAMSys.GetUser(claims.AccessKey)
|
||||
if !ok {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
return []byte(cred.SecretKey), nil
|
||||
|
||||
}
|
||||
|
||||
func isAuthTokenValid(token string) bool {
|
||||
@ -184,78 +131,40 @@ func isAuthTokenValid(token string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func webTokenAuthenticate(token string) (mapClaims, bool, error) {
|
||||
var claims = jwtgo.MapClaims{}
|
||||
func webTokenAuthenticate(token string) (*xjwt.MapClaims, bool, error) {
|
||||
if token == "" {
|
||||
return mapClaims{claims}, false, errNoAuthToken
|
||||
return nil, false, errNoAuthToken
|
||||
}
|
||||
jwtToken, err := parseJWTWithClaims(token, &claims)
|
||||
if err != nil {
|
||||
return mapClaims{claims}, false, err
|
||||
claims := xjwt.NewMapClaims()
|
||||
if err := xjwt.ParseWithClaims(token, claims, webTokenCallback); err != nil {
|
||||
return claims, false, errAuthentication
|
||||
}
|
||||
if !jwtToken.Valid {
|
||||
return mapClaims{claims}, false, errAuthentication
|
||||
}
|
||||
accessKey, ok := claims["accessKey"].(string)
|
||||
if !ok {
|
||||
accessKey, ok = claims["sub"].(string)
|
||||
if !ok {
|
||||
return mapClaims{claims}, false, errAuthentication
|
||||
}
|
||||
}
|
||||
owner := accessKey == globalActiveCred.AccessKey
|
||||
return mapClaims{claims}, owner, nil
|
||||
}
|
||||
|
||||
type mapClaims struct {
|
||||
jwtgo.MapClaims
|
||||
}
|
||||
|
||||
func (m mapClaims) Map() map[string]interface{} {
|
||||
return m.MapClaims
|
||||
}
|
||||
|
||||
func (m mapClaims) AccessKey() string {
|
||||
claimSub, ok := m.MapClaims["accessKey"].(string)
|
||||
if !ok {
|
||||
claimSub, _ = m.MapClaims["sub"].(string)
|
||||
}
|
||||
return claimSub
|
||||
owner := claims.AccessKey == globalActiveCred.AccessKey
|
||||
return claims, owner, nil
|
||||
}
|
||||
|
||||
// Check if the request is authenticated.
|
||||
// Returns nil if the request is authenticated. errNoAuthToken if token missing.
|
||||
// Returns errAuthentication for all other errors.
|
||||
func webRequestAuthenticate(req *http.Request) (mapClaims, bool, error) {
|
||||
var claims = jwtgo.MapClaims{}
|
||||
tokStr, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(req)
|
||||
func webRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, bool, error) {
|
||||
token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(req)
|
||||
if err != nil {
|
||||
if err == jwtreq.ErrNoTokenInRequest {
|
||||
return mapClaims{claims}, false, errNoAuthToken
|
||||
return nil, false, errNoAuthToken
|
||||
}
|
||||
return mapClaims{claims}, false, err
|
||||
return nil, false, err
|
||||
}
|
||||
jwtToken, err := parseJWTWithClaims(tokStr, &claims)
|
||||
if err != nil {
|
||||
return mapClaims{claims}, false, err
|
||||
claims := xjwt.NewMapClaims()
|
||||
if err := xjwt.ParseWithClaims(token, claims, webTokenCallback); err != nil {
|
||||
return claims, false, errAuthentication
|
||||
}
|
||||
if !jwtToken.Valid {
|
||||
return mapClaims{claims}, false, errAuthentication
|
||||
}
|
||||
accessKey, ok := claims["accessKey"].(string)
|
||||
if !ok {
|
||||
accessKey, ok = claims["sub"].(string)
|
||||
if !ok {
|
||||
return mapClaims{claims}, false, errAuthentication
|
||||
}
|
||||
}
|
||||
owner := accessKey == globalActiveCred.AccessKey
|
||||
return mapClaims{claims}, owner, nil
|
||||
owner := claims.AccessKey == globalActiveCred.AccessKey
|
||||
return claims, owner, nil
|
||||
}
|
||||
|
||||
func newAuthToken() string {
|
||||
func newAuthToken(audience string) string {
|
||||
cred := globalActiveCred
|
||||
token, err := authenticateNode(cred.AccessKey, cred.SecretKey)
|
||||
token, err := authenticateNode(cred.AccessKey, cred.SecretKey, audience)
|
||||
logger.CriticalIf(context.Background(), err)
|
||||
return token
|
||||
}
|
||||
|
371
cmd/jwt/parser.go
Normal file
371
cmd/jwt/parser.go
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jwt
|
||||
|
||||
// This file is a re-implementation of the original code here with some
|
||||
// additional allocation tweaks reproduced using GODEBUG=allocfreetrace=1
|
||||
// original file https://github.com/dgrijalva/jwt-go/blob/master/parser.go
|
||||
// borrowed under MIT License https://github.com/dgrijalva/jwt-go/blob/master/LICENSE
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// SigningMethodHMAC - Implements the HMAC-SHA family of signing methods signing methods
|
||||
// Expects key type of []byte for both signing and validation
|
||||
type SigningMethodHMAC struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
// Specific instances for HS256, HS384, HS512
|
||||
var (
|
||||
SigningMethodHS256 *SigningMethodHMAC
|
||||
SigningMethodHS384 *SigningMethodHMAC
|
||||
SigningMethodHS512 *SigningMethodHMAC
|
||||
)
|
||||
|
||||
var (
|
||||
base64BufPool sync.Pool
|
||||
hmacSigners []*SigningMethodHMAC
|
||||
)
|
||||
|
||||
func init() {
|
||||
base64BufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
buf := make([]byte, 1024)
|
||||
return &buf
|
||||
},
|
||||
}
|
||||
|
||||
hmacSigners = []*SigningMethodHMAC{
|
||||
{"HS256", crypto.SHA256},
|
||||
{"HS384", crypto.SHA384},
|
||||
{"HS512", crypto.SHA512},
|
||||
}
|
||||
}
|
||||
|
||||
// StandardClaims are basically standard claims with "accessKey"
|
||||
type StandardClaims struct {
|
||||
AccessKey string `json:"accessKey,omitempty"`
|
||||
jwtgo.StandardClaims
|
||||
}
|
||||
|
||||
// MapClaims - implements custom unmarshaller
|
||||
type MapClaims struct {
|
||||
AccessKey string `json:"accessKey,omitempty"`
|
||||
jwtgo.MapClaims
|
||||
}
|
||||
|
||||
// NewStandardClaims - initializes standard claims
|
||||
func NewStandardClaims() *StandardClaims {
|
||||
return &StandardClaims{}
|
||||
}
|
||||
|
||||
// SetIssuer sets issuer for these claims
|
||||
func (c *StandardClaims) SetIssuer(issuer string) {
|
||||
c.Issuer = issuer
|
||||
}
|
||||
|
||||
// SetAudience sets audience for these claims
|
||||
func (c *StandardClaims) SetAudience(aud string) {
|
||||
c.Audience = aud
|
||||
}
|
||||
|
||||
// SetExpiry sets expiry in unix epoch secs
|
||||
func (c *StandardClaims) SetExpiry(t time.Time) {
|
||||
c.ExpiresAt = t.Unix()
|
||||
}
|
||||
|
||||
// SetAccessKey sets access key as jwt subject and custom
|
||||
// "accessKey" field.
|
||||
func (c *StandardClaims) SetAccessKey(accessKey string) {
|
||||
c.Subject = accessKey
|
||||
c.AccessKey = accessKey
|
||||
}
|
||||
|
||||
// Valid - implements https://godoc.org/github.com/dgrijalva/jwt-go#Claims compatible
|
||||
// claims interface, additionally validates "accessKey" fields.
|
||||
func (c *StandardClaims) Valid() error {
|
||||
if err := c.StandardClaims.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.AccessKey == "" && c.Subject == "" {
|
||||
return jwtgo.NewValidationError("accessKey/sub missing",
|
||||
jwtgo.ValidationErrorClaimsInvalid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewMapClaims - Initializes a new map claims
|
||||
func NewMapClaims() *MapClaims {
|
||||
return &MapClaims{MapClaims: jwtgo.MapClaims{}}
|
||||
}
|
||||
|
||||
// Lookup returns the value and if the key is found.
|
||||
func (c *MapClaims) Lookup(key string) (value string, ok bool) {
|
||||
var vinterface interface{}
|
||||
vinterface, ok = c.MapClaims[key]
|
||||
if ok {
|
||||
value, ok = vinterface.(string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetExpiry sets expiry in unix epoch secs
|
||||
func (c *MapClaims) SetExpiry(t time.Time) {
|
||||
c.MapClaims["exp"] = t.Unix()
|
||||
}
|
||||
|
||||
// SetAccessKey sets access key as jwt subject and custom
|
||||
// "accessKey" field.
|
||||
func (c *MapClaims) SetAccessKey(accessKey string) {
|
||||
c.MapClaims["sub"] = accessKey
|
||||
c.MapClaims["accessKey"] = accessKey
|
||||
}
|
||||
|
||||
// Valid - implements https://godoc.org/github.com/dgrijalva/jwt-go#Claims compatible
|
||||
// claims interface, additionally validates "accessKey" fields.
|
||||
func (c *MapClaims) Valid() error {
|
||||
if err := c.MapClaims.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.AccessKey == "" {
|
||||
return jwtgo.NewValidationError("accessKey/sub missing",
|
||||
jwtgo.ValidationErrorClaimsInvalid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Map returns underlying low-level map claims.
|
||||
func (c *MapClaims) Map() map[string]interface{} {
|
||||
return c.MapClaims
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the MapClaims struct
|
||||
func (c *MapClaims) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(c.MapClaims)
|
||||
}
|
||||
|
||||
// ParseWithStandardClaims - parse the token string, valid methods.
|
||||
func ParseWithStandardClaims(tokenStr string, claims *StandardClaims, key []byte) error {
|
||||
// Key is not provided.
|
||||
if key == nil {
|
||||
// keyFunc was not provided, return error.
|
||||
return jwtgo.NewValidationError("no key was provided.", jwtgo.ValidationErrorUnverifiable)
|
||||
}
|
||||
|
||||
bufp := base64BufPool.Get().(*[]byte)
|
||||
defer base64BufPool.Put(bufp)
|
||||
|
||||
signer, err := ParseUnverifiedStandardClaims(tokenStr, claims, *bufp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i := strings.LastIndex(tokenStr, ".")
|
||||
if i < 0 {
|
||||
return jwtgo.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
n, err := base64Decode(tokenStr[i+1:], *bufp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasher := hmac.New(signer.Hash.New, key)
|
||||
hasher.Write([]byte(tokenStr[:i]))
|
||||
if !hmac.Equal((*bufp)[:n], hasher.Sum(nil)) {
|
||||
return jwtgo.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
if claims.AccessKey == "" && claims.Subject == "" {
|
||||
return jwtgo.NewValidationError("accessKey/sub missing",
|
||||
jwtgo.ValidationErrorClaimsInvalid)
|
||||
}
|
||||
|
||||
// Signature is valid, lets validate the claims for
|
||||
// other fields such as expiry etc.
|
||||
return claims.Valid()
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc7519#page-11
|
||||
type jwtHeader struct {
|
||||
Algorithm string `json:"alg"`
|
||||
Type string `json:"typ"`
|
||||
}
|
||||
|
||||
// ParseUnverifiedStandardClaims - WARNING: Don't use this method unless you know what you're doing
|
||||
//
|
||||
// This method parses the token but doesn't validate the signature. It's only
|
||||
// ever useful in cases where you know the signature is valid (because it has
|
||||
// been checked previously in the stack) and you want to extract values from
|
||||
// it.
|
||||
func ParseUnverifiedStandardClaims(tokenString string, claims *StandardClaims, buf []byte) (*SigningMethodHMAC, error) {
|
||||
if strings.Count(tokenString, ".") != 2 {
|
||||
return nil, jwtgo.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
i := strings.Index(tokenString, ".")
|
||||
j := strings.LastIndex(tokenString, ".")
|
||||
|
||||
n, err := base64Decode(tokenString[:i], buf)
|
||||
if err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
var header = jwtHeader{}
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
if err = json.Unmarshal(buf[:n], &header); err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
n, err = base64Decode(tokenString[i+1:j], buf)
|
||||
if err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(buf[:n], claims); err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
for _, signer := range hmacSigners {
|
||||
if header.Algorithm == signer.Name {
|
||||
return signer, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, jwtgo.NewValidationError(fmt.Sprintf("signing method (%s) is unavailable.", header.Algorithm),
|
||||
jwtgo.ValidationErrorUnverifiable)
|
||||
}
|
||||
|
||||
// ParseWithClaims - parse the token string, valid methods.
|
||||
func ParseWithClaims(tokenStr string, claims *MapClaims, fn func(*MapClaims) ([]byte, error)) error {
|
||||
// Key lookup function has to be provided.
|
||||
if fn == nil {
|
||||
// keyFunc was not provided, return error.
|
||||
return jwtgo.NewValidationError("no Keyfunc was provided.", jwtgo.ValidationErrorUnverifiable)
|
||||
}
|
||||
|
||||
bufp := base64BufPool.Get().(*[]byte)
|
||||
defer base64BufPool.Put(bufp)
|
||||
|
||||
signer, err := ParseUnverifiedMapClaims(tokenStr, claims, *bufp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i := strings.LastIndex(tokenStr, ".")
|
||||
if i < 0 {
|
||||
return jwtgo.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
n, err := base64Decode(tokenStr[i+1:], *bufp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
claims.AccessKey, ok = claims.Lookup("accessKey")
|
||||
if !ok {
|
||||
claims.AccessKey, ok = claims.Lookup("sub")
|
||||
if !ok {
|
||||
return jwtgo.NewValidationError("accessKey/sub missing",
|
||||
jwtgo.ValidationErrorClaimsInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup key from claims, claims may not be valid and may return
|
||||
// invalid key which is okay as the signature verification will fail.
|
||||
key, err := fn(claims)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasher := hmac.New(signer.Hash.New, key)
|
||||
hasher.Write([]byte(tokenStr[:i]))
|
||||
if !hmac.Equal((*bufp)[:n], hasher.Sum(nil)) {
|
||||
return jwtgo.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
// Signature is valid, lets validate the claims for
|
||||
// other fields such as expiry etc.
|
||||
return claims.Valid()
|
||||
}
|
||||
|
||||
// base64Decode returns the bytes represented by the base64 string s.
|
||||
func base64Decode(s string, buf []byte) (int, error) {
|
||||
return base64.RawURLEncoding.Decode(buf, []byte(s))
|
||||
}
|
||||
|
||||
// ParseUnverifiedMapClaims - WARNING: Don't use this method unless you know what you're doing
|
||||
//
|
||||
// This method parses the token but doesn't validate the signature. It's only
|
||||
// ever useful in cases where you know the signature is valid (because it has
|
||||
// been checked previously in the stack) and you want to extract values from
|
||||
// it.
|
||||
func ParseUnverifiedMapClaims(tokenString string, claims *MapClaims, buf []byte) (*SigningMethodHMAC, error) {
|
||||
if strings.Count(tokenString, ".") != 2 {
|
||||
return nil, jwtgo.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
i := strings.Index(tokenString, ".")
|
||||
j := strings.LastIndex(tokenString, ".")
|
||||
|
||||
n, err := base64Decode(tokenString[:i], buf)
|
||||
if err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
var header = jwtHeader{}
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
if err = json.Unmarshal(buf[:n], &header); err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
n, err = base64Decode(tokenString[i+1:j], buf)
|
||||
if err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(buf[:n], &claims.MapClaims); err != nil {
|
||||
return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
for _, signer := range hmacSigners {
|
||||
if header.Algorithm == signer.Name {
|
||||
return signer, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, jwtgo.NewValidationError(fmt.Sprintf("signing method (%s) is unavailable.", header.Algorithm),
|
||||
jwtgo.ValidationErrorUnverifiable)
|
||||
}
|
214
cmd/jwt/parser_test.go
Normal file
214
cmd/jwt/parser_test.go
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jwt
|
||||
|
||||
// This file is a re-implementation of the original code here with some
|
||||
// additional allocation tweaks reproduced using GODEBUG=allocfreetrace=1
|
||||
// original file https://github.com/dgrijalva/jwt-go/blob/master/parser.go
|
||||
// borrowed under MIT License https://github.com/dgrijalva/jwt-go/blob/master/LICENSE
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultKeyFunc = func(claim *MapClaims) ([]byte, error) { return []byte("HelloSecret"), nil }
|
||||
emptyKeyFunc = func(claim *MapClaims) ([]byte, error) { return nil, nil }
|
||||
errorKeyFunc = func(claim *MapClaims) ([]byte, error) { return nil, fmt.Errorf("error loading key") }
|
||||
)
|
||||
|
||||
var jwtTestData = []struct {
|
||||
name string
|
||||
tokenString string
|
||||
keyfunc func(*MapClaims) ([]byte, error)
|
||||
claims jwt.Claims
|
||||
valid bool
|
||||
errors int32
|
||||
}{
|
||||
{
|
||||
"basic",
|
||||
"",
|
||||
defaultKeyFunc,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
true,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"basic expired",
|
||||
"", // autogen
|
||||
defaultKeyFunc,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
"exp": float64(time.Now().Unix() - 100),
|
||||
},
|
||||
},
|
||||
false,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
"basic nbf",
|
||||
"", // autogen
|
||||
defaultKeyFunc,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
"nbf": float64(time.Now().Unix() + 100),
|
||||
},
|
||||
},
|
||||
false,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
"expired and nbf",
|
||||
"", // autogen
|
||||
defaultKeyFunc,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
"nbf": float64(time.Now().Unix() + 100),
|
||||
"exp": float64(time.Now().Unix() - 100),
|
||||
},
|
||||
},
|
||||
false,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
"basic invalid",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
defaultKeyFunc,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
false,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
"basic nokeyfunc",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
nil,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
false,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
"basic nokey",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
emptyKeyFunc,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
false,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
"basic errorkey",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
errorKeyFunc,
|
||||
&MapClaims{
|
||||
MapClaims: jwtgo.MapClaims{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
false,
|
||||
-1,
|
||||
},
|
||||
{
|
||||
"Standard Claims",
|
||||
"",
|
||||
defaultKeyFunc,
|
||||
&StandardClaims{
|
||||
StandardClaims: jwtgo.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(time.Second * 10).Unix(),
|
||||
},
|
||||
},
|
||||
true,
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
func mapClaimsToken(claims *MapClaims) string {
|
||||
claims.SetAccessKey("test")
|
||||
j := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
|
||||
tk, _ := j.SignedString([]byte("HelloSecret"))
|
||||
return tk
|
||||
}
|
||||
|
||||
func standardClaimsToken(claims *StandardClaims) string {
|
||||
claims.AccessKey = "test"
|
||||
claims.Subject = "test"
|
||||
j := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
|
||||
tk, _ := j.SignedString([]byte("HelloSecret"))
|
||||
return tk
|
||||
}
|
||||
|
||||
func TestParserParse(t *testing.T) {
|
||||
// Iterate over test data set and run tests
|
||||
for _, data := range jwtTestData {
|
||||
data := data
|
||||
t.Run(data.name, func(t *testing.T) {
|
||||
// Parse the token
|
||||
var err error
|
||||
|
||||
// Figure out correct claims type
|
||||
switch claims := data.claims.(type) {
|
||||
case *MapClaims:
|
||||
if data.tokenString == "" {
|
||||
data.tokenString = mapClaimsToken(claims)
|
||||
}
|
||||
err = ParseWithClaims(data.tokenString, &MapClaims{}, data.keyfunc)
|
||||
case *StandardClaims:
|
||||
if data.tokenString == "" {
|
||||
data.tokenString = standardClaimsToken(claims)
|
||||
}
|
||||
err = ParseWithStandardClaims(data.tokenString, &StandardClaims{}, []byte("HelloSecret"))
|
||||
}
|
||||
|
||||
if data.valid && err != nil {
|
||||
t.Errorf("Error while verifying token: %T:%v", err, err)
|
||||
}
|
||||
|
||||
if !data.valid && err == nil {
|
||||
t.Errorf("Invalid token passed validation")
|
||||
}
|
||||
|
||||
if data.errors != 0 {
|
||||
_, ok := err.(*jwt.ValidationError)
|
||||
if !ok {
|
||||
t.Errorf("Expected *jwt.ValidationError, but got %#v instead", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
xjwt "github.com/minio/minio/cmd/jwt"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
)
|
||||
|
||||
@ -62,9 +63,7 @@ func testAuthenticate(authType string, t *testing.T) {
|
||||
// Run tests.
|
||||
for _, testCase := range testCases {
|
||||
var err error
|
||||
if authType == "node" {
|
||||
_, err = authenticateNode(testCase.accessKey, testCase.secretKey)
|
||||
} else if authType == "web" {
|
||||
if authType == "web" {
|
||||
_, err = authenticateWeb(testCase.accessKey, testCase.secretKey)
|
||||
} else if authType == "url" {
|
||||
_, err = authenticateURL(testCase.accessKey, testCase.secretKey)
|
||||
@ -83,10 +82,6 @@ func testAuthenticate(authType string, t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticateNode(t *testing.T) {
|
||||
testAuthenticate("node", t)
|
||||
}
|
||||
|
||||
func TestAuthenticateWeb(t *testing.T) {
|
||||
testAuthenticate("web", t)
|
||||
}
|
||||
@ -150,6 +145,64 @@ func TestWebRequestAuthenticate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseJWTStandardClaims(b *testing.B) {
|
||||
obj, fsDir, err := prepareFS()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(fsDir)
|
||||
if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
creds := globalActiveCred
|
||||
token, err := authenticateNode(creds.AccessKey, creds.SecretKey, "")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
err = xjwt.ParseWithStandardClaims(token, xjwt.NewStandardClaims(), []byte(creds.SecretKey))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkParseJWTMapClaims(b *testing.B) {
|
||||
obj, fsDir, err := prepareFS()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(fsDir)
|
||||
if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
creds := globalActiveCred
|
||||
token, err := authenticateNode(creds.AccessKey, creds.SecretKey, "")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
err = xjwt.ParseWithClaims(token, xjwt.NewMapClaims(), func(*xjwt.MapClaims) ([]byte, error) {
|
||||
return []byte(creds.SecretKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkAuthenticateNode(b *testing.B) {
|
||||
obj, fsDir, err := prepareFS()
|
||||
if err != nil {
|
||||
@ -164,7 +217,7 @@ func BenchmarkAuthenticateNode(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||
authenticateNode(creds.AccessKey, creds.SecretKey, "")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ func createLockTestServer(t *testing.T) (string, *lockRESTServer, string) {
|
||||
},
|
||||
}
|
||||
creds := globalActiveCred
|
||||
token, err := authenticateNode(creds.AccessKey, creds.SecretKey)
|
||||
token, err := authenticateNode(creds.AccessKey, creds.SecretKey, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ type Client struct {
|
||||
httpClient *http.Client
|
||||
httpIdleConnsCloser func()
|
||||
url *url.URL
|
||||
newAuthToken func() string
|
||||
newAuthToken func(audience string) string
|
||||
}
|
||||
|
||||
// URL query separator constants
|
||||
@ -62,7 +62,7 @@ func (c *Client) CallWithContext(ctx context.Context, method string, values url.
|
||||
return nil, &NetworkError{err}
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Authorization", "Bearer "+c.newAuthToken())
|
||||
req.Header.Set("Authorization", "Bearer "+c.newAuthToken(req.URL.Query().Encode()))
|
||||
req.Header.Set("X-Minio-Time", time.Now().UTC().Format(time.RFC3339))
|
||||
if length > 0 {
|
||||
req.ContentLength = length
|
||||
@ -102,7 +102,7 @@ func (c *Client) Close() {
|
||||
}
|
||||
|
||||
// NewClient - returns new REST client.
|
||||
func NewClient(url *url.URL, newCustomTransport func() *http.Transport, newAuthToken func() string) (*Client, error) {
|
||||
func NewClient(url *url.URL, newCustomTransport func() *http.Transport, newAuthToken func(aud string) string) (*Client, error) {
|
||||
// Transport is exactly same as Go default in https://golang.org/pkg/net/http/#RoundTripper
|
||||
// except custom DialContext and TLSClientConfig.
|
||||
tr := newCustomTransport()
|
||||
|
@ -30,9 +30,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jwtreq "github.com/dgrijalva/jwt-go/request"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/minio/minio/cmd/config"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
xjwt "github.com/minio/minio/cmd/jwt"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
)
|
||||
|
||||
@ -54,11 +56,29 @@ const DefaultSkewTime = 15 * time.Minute
|
||||
|
||||
// Authenticates storage client's requests and validates for skewed time.
|
||||
func storageServerRequestValidate(r *http.Request) error {
|
||||
_, owner, err := webRequestAuthenticate(r)
|
||||
token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(r)
|
||||
if err != nil {
|
||||
if err == jwtreq.ErrNoTokenInRequest {
|
||||
return errNoAuthToken
|
||||
}
|
||||
return err
|
||||
}
|
||||
if !owner { // Disable access for non-admin users.
|
||||
|
||||
claims := xjwt.NewStandardClaims()
|
||||
if err = xjwt.ParseWithStandardClaims(token, claims, []byte(globalActiveCred.SecretKey)); err != nil {
|
||||
return errAuthentication
|
||||
}
|
||||
|
||||
owner := claims.AccessKey == globalActiveCred.AccessKey || claims.Subject == globalActiveCred.AccessKey
|
||||
if !owner {
|
||||
return errAuthentication
|
||||
}
|
||||
|
||||
if claims.Audience != r.URL.Query().Encode() {
|
||||
return errAuthentication
|
||||
}
|
||||
|
||||
if claims.Issuer != ReleaseTag {
|
||||
return errAuthentication
|
||||
}
|
||||
|
||||
@ -70,11 +90,12 @@ func storageServerRequestValidate(r *http.Request) error {
|
||||
utcNow := UTCNow()
|
||||
delta := requestTime.Sub(utcNow)
|
||||
if delta < 0 {
|
||||
delta = delta * -1
|
||||
delta *= -1
|
||||
}
|
||||
if delta > DefaultSkewTime {
|
||||
return fmt.Errorf("client time %v is too apart with server time %v", requestTime, utcNow)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, rep
|
||||
}
|
||||
|
||||
if !owner {
|
||||
creds, ok := globalIAMSys.GetUser(claims.AccessKey())
|
||||
creds, ok := globalIAMSys.GetUser(claims.AccessKey)
|
||||
if ok && creds.SessionToken != "" {
|
||||
reply.MinioUserInfo["isTempUser"] = true
|
||||
}
|
||||
@ -154,10 +154,10 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep
|
||||
|
||||
// For authenticated users apply IAM policy.
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.CreateBucketAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
Claims: claims.Map(),
|
||||
}) {
|
||||
@ -216,10 +216,10 @@ func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs,
|
||||
|
||||
// For authenticated users apply IAM policy.
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.DeleteBucketAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
Claims: claims.Map(),
|
||||
}) {
|
||||
@ -319,10 +319,10 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re
|
||||
}
|
||||
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.ListBucketAction,
|
||||
BucketName: dnsRecord.Key,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: "",
|
||||
Claims: claims.Map(),
|
||||
@ -342,10 +342,10 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re
|
||||
}
|
||||
for _, bucket := range buckets {
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.ListBucketAction,
|
||||
BucketName: bucket.Name,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: "",
|
||||
Claims: claims.Map(),
|
||||
@ -495,19 +495,19 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
||||
r.Header.Set("delimiter", SlashSeparator)
|
||||
|
||||
readable := globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.ListBucketAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
Claims: claims.Map(),
|
||||
})
|
||||
|
||||
writable := globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.PutObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: args.Prefix + SlashSeparator,
|
||||
Claims: claims.Map(),
|
||||
@ -675,10 +675,10 @@ next:
|
||||
govBypassPerms := ErrAccessDenied
|
||||
if authErr != errNoAuthToken {
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.DeleteObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: objectName,
|
||||
Claims: claims.Map(),
|
||||
@ -686,10 +686,10 @@ next:
|
||||
return toJSONError(ctx, errAccessDenied)
|
||||
}
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.BypassGovernanceRetentionAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: objectName,
|
||||
Claims: claims.Map(),
|
||||
@ -719,10 +719,10 @@ next:
|
||||
}
|
||||
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.DeleteObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: objectName,
|
||||
Claims: claims.Map(),
|
||||
@ -844,8 +844,8 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se
|
||||
}
|
||||
|
||||
// for IAM users, access key cannot be updated
|
||||
// claims.AccessKey() is used instead of accesskey from args
|
||||
prevCred, ok := globalIAMSys.GetUser(claims.AccessKey())
|
||||
// claims.AccessKey is used instead of accesskey from args
|
||||
prevCred, ok := globalIAMSys.GetUser(claims.AccessKey)
|
||||
if !ok {
|
||||
return errInvalidAccessKeyID
|
||||
}
|
||||
@ -855,7 +855,7 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se
|
||||
return errIncorrectCreds
|
||||
}
|
||||
|
||||
creds, err := auth.CreateCredentials(claims.AccessKey(), args.NewSecretKey)
|
||||
creds, err := auth.CreateCredentials(claims.AccessKey, args.NewSecretKey)
|
||||
if err != nil {
|
||||
return toJSONError(ctx, err)
|
||||
}
|
||||
@ -892,7 +892,7 @@ func (web *webAPIHandlers) CreateURLToken(r *http.Request, args *WebGenericArgs,
|
||||
creds := globalActiveCred
|
||||
if !owner {
|
||||
var ok bool
|
||||
creds, ok = globalIAMSys.GetUser(claims.AccessKey())
|
||||
creds, ok = globalIAMSys.GetUser(claims.AccessKey)
|
||||
if !ok {
|
||||
return toJSONError(ctx, errInvalidAccessKeyID)
|
||||
}
|
||||
@ -954,10 +954,10 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
// For authenticated users apply IAM policy.
|
||||
if authErr == nil {
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.PutObjectAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
Claims: claims.Map(),
|
||||
@ -966,10 +966,10 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.PutObjectRetentionAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
Claims: claims.Map(),
|
||||
@ -977,10 +977,10 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
retPerms = ErrNone
|
||||
}
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.PutObjectLegalHoldAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
Claims: claims.Map(),
|
||||
@ -1188,10 +1188,10 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
||||
// For authenticated users apply IAM policy.
|
||||
if authErr == nil {
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetObjectAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
Claims: claims.Map(),
|
||||
@ -1200,10 +1200,10 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetObjectRetentionAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
Claims: claims.Map(),
|
||||
@ -1211,10 +1211,10 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
||||
getRetPerms = ErrNone
|
||||
}
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetObjectLegalHoldAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
Claims: claims.Map(),
|
||||
@ -1390,10 +1390,10 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
||||
if authErr == nil {
|
||||
for _, object := range args.Objects {
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: pathJoin(args.Prefix, object),
|
||||
Claims: claims.Map(),
|
||||
@ -1403,10 +1403,10 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
retentionPerm := ErrAccessDenied
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetObjectRetentionAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: pathJoin(args.Prefix, object),
|
||||
Claims: claims.Map(),
|
||||
@ -1417,10 +1417,10 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
legalHoldPerm := ErrAccessDenied
|
||||
if globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetObjectLegalHoldAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: pathJoin(args.Prefix, object),
|
||||
Claims: claims.Map(),
|
||||
@ -1570,10 +1570,10 @@ func (web *webAPIHandlers) GetBucketPolicy(r *http.Request, args *GetBucketPolic
|
||||
|
||||
// For authenticated users apply IAM policy.
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetBucketPolicyAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
Claims: claims.Map(),
|
||||
}) {
|
||||
@ -1668,10 +1668,10 @@ func (web *webAPIHandlers) ListAllBucketPolicies(r *http.Request, args *ListAllB
|
||||
|
||||
// For authenticated users apply IAM policy.
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.GetBucketPolicyAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
Claims: claims.Map(),
|
||||
}) {
|
||||
@ -1759,10 +1759,10 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
|
||||
|
||||
// For authenticated users apply IAM policy.
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey(),
|
||||
AccountName: claims.AccessKey,
|
||||
Action: iampolicy.PutBucketPolicyAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey(), claims.Map()),
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
Claims: claims.Map(),
|
||||
}) {
|
||||
@ -1905,7 +1905,7 @@ func (web *webAPIHandlers) PresignedGet(r *http.Request, args *PresignedGetArgs,
|
||||
var creds auth.Credentials
|
||||
if !owner {
|
||||
var ok bool
|
||||
creds, ok = globalIAMSys.GetUser(claims.AccessKey())
|
||||
creds, ok = globalIAMSys.GetUser(claims.AccessKey)
|
||||
if !ok {
|
||||
return toJSONError(ctx, errInvalidAccessKeyID)
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
miniogopolicy "github.com/minio/minio-go/v6/pkg/policy"
|
||||
xjwt "github.com/minio/minio/cmd/jwt"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
"github.com/minio/minio/pkg/bucket/policy"
|
||||
"github.com/minio/minio/pkg/bucket/policy/condition"
|
||||
@ -753,12 +754,10 @@ func TestWebCreateURLToken(t *testing.T) {
|
||||
}
|
||||
|
||||
func getTokenString(accessKey, secretKey string) (string, error) {
|
||||
utcNow := UTCNow()
|
||||
mapClaims := jwtgo.MapClaims{}
|
||||
mapClaims["exp"] = utcNow.Add(defaultJWTExpiry).Unix()
|
||||
mapClaims["sub"] = accessKey
|
||||
mapClaims["accessKey"] = accessKey
|
||||
token := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, mapClaims)
|
||||
claims := xjwt.NewMapClaims()
|
||||
claims.SetExpiry(UTCNow().Add(defaultJWTExpiry))
|
||||
claims.SetAccessKey(accessKey)
|
||||
token := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, claims)
|
||||
return token.SignedString([]byte(secretKey))
|
||||
}
|
||||
|
||||
@ -1002,7 +1001,7 @@ func testDownloadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandl
|
||||
}
|
||||
|
||||
if !bytes.Equal(bodyContent, bytes.NewBufferString("Authentication failed, check your access credentials").Bytes()) {
|
||||
t.Fatalf("Expected authentication error message, got %v", bodyContent)
|
||||
t.Fatalf("Expected authentication error message, got %s", string(bodyContent))
|
||||
}
|
||||
|
||||
// Unauthenticated download should fail.
|
||||
|
Loading…
Reference in New Issue
Block a user