mirror of https://github.com/minio/minio.git
Add support for {jwt:sub} substitutions for policies (#8393)
Fixes #8345
This commit is contained in:
parent
f2cc97a44c
commit
5afb1b6747
|
@ -334,7 +334,7 @@ func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, actio
|
|||
AccountName: cred.AccessKey,
|
||||
Action: action,
|
||||
BucketName: bucketName,
|
||||
ConditionValues: getConditionValues(r, locationConstraint, ""),
|
||||
ConditionValues: getConditionValues(r, locationConstraint, "", nil),
|
||||
IsOwner: false,
|
||||
ObjectName: objectName,
|
||||
}) {
|
||||
|
@ -348,7 +348,7 @@ func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, actio
|
|||
AccountName: cred.AccessKey,
|
||||
Action: iampolicy.Action(action),
|
||||
BucketName: bucketName,
|
||||
ConditionValues: getConditionValues(r, "", cred.AccessKey),
|
||||
ConditionValues: getConditionValues(r, "", cred.AccessKey, claims),
|
||||
ObjectName: objectName,
|
||||
IsOwner: owner,
|
||||
Claims: claims,
|
||||
|
@ -502,7 +502,7 @@ func isPutAllowed(atype authType, bucketName, objectName string, r *http.Request
|
|||
AccountName: cred.AccessKey,
|
||||
Action: policy.PutObjectAction,
|
||||
BucketName: bucketName,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
ObjectName: objectName,
|
||||
}) {
|
||||
|
@ -515,7 +515,7 @@ func isPutAllowed(atype authType, bucketName, objectName string, r *http.Request
|
|||
AccountName: cred.AccessKey,
|
||||
Action: policy.PutObjectAction,
|
||||
BucketName: bucketName,
|
||||
ConditionValues: getConditionValues(r, "", cred.AccessKey),
|
||||
ConditionValues: getConditionValues(r, "", cred.AccessKey, claims),
|
||||
ObjectName: objectName,
|
||||
IsOwner: owner,
|
||||
Claims: claims,
|
||||
|
|
|
@ -296,7 +296,7 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
|
|||
AccountName: accessKey,
|
||||
Action: iampolicy.ListBucketAction,
|
||||
BucketName: bucketInfo.Name,
|
||||
ConditionValues: getConditionValues(r, "", accessKey),
|
||||
ConditionValues: getConditionValues(r, "", accessKey, claims),
|
||||
IsOwner: owner,
|
||||
ObjectName: "",
|
||||
Claims: claims,
|
||||
|
|
36
cmd/jwt.go
36
cmd/jwt.go
|
@ -161,44 +161,58 @@ func isAuthTokenValid(token string) bool {
|
|||
return err == nil
|
||||
}
|
||||
|
||||
func webTokenAuthenticate(token string) (jwtgo.StandardClaims, bool, error) {
|
||||
func webTokenAuthenticate(token string) (standardClaims, bool, error) {
|
||||
var claims = jwtgo.StandardClaims{}
|
||||
if token == "" {
|
||||
return claims, false, errNoAuthToken
|
||||
return standardClaims{claims}, false, errNoAuthToken
|
||||
}
|
||||
|
||||
jwtToken, err := parseJWTWithClaims(token, &claims)
|
||||
if err != nil {
|
||||
return claims, false, err
|
||||
return standardClaims{claims}, false, err
|
||||
}
|
||||
if !jwtToken.Valid {
|
||||
return claims, false, errAuthentication
|
||||
return standardClaims{claims}, false, errAuthentication
|
||||
}
|
||||
owner := claims.Subject == globalServerConfig.GetCredential().AccessKey
|
||||
return claims, owner, nil
|
||||
return standardClaims{claims}, owner, nil
|
||||
}
|
||||
|
||||
// jwt standardClaims
|
||||
type standardClaims struct {
|
||||
jwtgo.StandardClaims
|
||||
}
|
||||
|
||||
func (s standardClaims) Map() map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
m["sub"] = s.Subject
|
||||
m["iss"] = s.Issuer
|
||||
m["aud"] = s.Audience
|
||||
m["jti"] = s.Id
|
||||
return m
|
||||
}
|
||||
|
||||
// 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) (jwtgo.StandardClaims, bool, error) {
|
||||
func webRequestAuthenticate(req *http.Request) (standardClaims, bool, error) {
|
||||
var claims = jwtgo.StandardClaims{}
|
||||
tokStr, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(req)
|
||||
if err != nil {
|
||||
if err == jwtreq.ErrNoTokenInRequest {
|
||||
return claims, false, errNoAuthToken
|
||||
return standardClaims{claims}, false, errNoAuthToken
|
||||
}
|
||||
return claims, false, err
|
||||
return standardClaims{claims}, false, err
|
||||
}
|
||||
jwtToken, err := parseJWTWithClaims(tokStr, &claims)
|
||||
if err != nil {
|
||||
return claims, false, err
|
||||
return standardClaims{claims}, false, err
|
||||
}
|
||||
if !jwtToken.Valid {
|
||||
return claims, false, errAuthentication
|
||||
return standardClaims{claims}, false, errAuthentication
|
||||
}
|
||||
owner := claims.Subject == globalServerConfig.GetCredential().AccessKey
|
||||
return claims, owner, nil
|
||||
return standardClaims{claims}, owner, nil
|
||||
}
|
||||
|
||||
func newAuthToken() string {
|
||||
|
|
|
@ -134,7 +134,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
|||
if globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.ListBucketAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
}) {
|
||||
_, err = getObjectInfo(ctx, bucket, object, opts)
|
||||
|
@ -293,7 +293,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
|||
if globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.ListBucketAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
}) {
|
||||
getObjectInfo := objectAPI.GetObjectInfo
|
||||
|
@ -466,7 +466,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
|||
if globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.ListBucketAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
}) {
|
||||
_, err = getObjectInfo(ctx, bucket, object, opts)
|
||||
|
|
|
@ -167,7 +167,7 @@ func NewPolicySys() *PolicySys {
|
|||
}
|
||||
}
|
||||
|
||||
func getConditionValues(request *http.Request, locationConstraint string, username string) map[string][]string {
|
||||
func getConditionValues(request *http.Request, locationConstraint string, username string, claims map[string]interface{}) map[string][]string {
|
||||
currTime := UTCNow()
|
||||
principalType := func() string {
|
||||
if username != "" {
|
||||
|
@ -207,6 +207,13 @@ func getConditionValues(request *http.Request, locationConstraint string, userna
|
|||
args["LocationConstraint"] = []string{locationConstraint}
|
||||
}
|
||||
|
||||
// JWT specific values
|
||||
for k, v := range claims {
|
||||
vStr, ok := v.(string)
|
||||
if ok {
|
||||
args[k] = []string{vStr}
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.CreateBucketAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
}) {
|
||||
return toJSONError(ctx, errAccessDenied)
|
||||
|
@ -221,7 +221,7 @@ func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs,
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.DeleteBucketAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
}) {
|
||||
return toJSONError(ctx, errAccessDenied)
|
||||
|
@ -325,7 +325,7 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.ListBucketAction,
|
||||
BucketName: dnsRecord.Key,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: "",
|
||||
}) {
|
||||
|
@ -347,7 +347,7 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.ListBucketAction,
|
||||
BucketName: bucket.Name,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: "",
|
||||
}) {
|
||||
|
@ -459,7 +459,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||
readable := globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.ListBucketAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
})
|
||||
|
||||
|
@ -467,7 +467,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||
writable := globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.PutObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
ObjectName: args.Prefix + SlashSeparator,
|
||||
})
|
||||
|
@ -498,7 +498,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.ListBucketAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
})
|
||||
|
||||
|
@ -506,7 +506,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.PutObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: args.Prefix + SlashSeparator,
|
||||
})
|
||||
|
@ -598,7 +598,7 @@ func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs,
|
|||
if !globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.DeleteObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
ObjectName: object,
|
||||
}) {
|
||||
|
@ -672,7 +672,7 @@ next:
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.DeleteObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: objectName,
|
||||
}) {
|
||||
|
@ -690,7 +690,7 @@ next:
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.DeleteObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: objectName,
|
||||
}) {
|
||||
|
@ -930,7 +930,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||
if !globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.PutObjectAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
ObjectName: object,
|
||||
}) {
|
||||
|
@ -949,7 +949,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.PutObjectAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
}) {
|
||||
|
@ -1110,7 +1110,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||
if !globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.GetObjectAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
ObjectName: object,
|
||||
}) {
|
||||
|
@ -1129,7 +1129,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.GetObjectAction,
|
||||
BucketName: bucket,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: object,
|
||||
}) {
|
||||
|
@ -1259,7 +1259,7 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
|||
if !globalPolicySys.IsAllowed(policy.Args{
|
||||
Action: policy.GetObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", ""),
|
||||
ConditionValues: getConditionValues(r, "", "", nil),
|
||||
IsOwner: false,
|
||||
ObjectName: pathJoin(args.Prefix, object),
|
||||
}) {
|
||||
|
@ -1280,7 +1280,7 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.GetObjectAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
ObjectName: pathJoin(args.Prefix, object),
|
||||
}) {
|
||||
|
@ -1426,7 +1426,7 @@ func (web *webAPIHandlers) GetBucketPolicy(r *http.Request, args *GetBucketPolic
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.GetBucketPolicyAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
}) {
|
||||
return toJSONError(ctx, errAccessDenied)
|
||||
|
@ -1523,7 +1523,7 @@ func (web *webAPIHandlers) ListAllBucketPolicies(r *http.Request, args *ListAllB
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.GetBucketPolicyAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
}) {
|
||||
return toJSONError(ctx, errAccessDenied)
|
||||
|
@ -1613,7 +1613,7 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
|
|||
AccountName: claims.Subject,
|
||||
Action: iampolicy.PutBucketPolicyAction,
|
||||
BucketName: args.BucketName,
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject),
|
||||
ConditionValues: getConditionValues(r, "", claims.Subject, claims.Map()),
|
||||
IsOwner: owner,
|
||||
}) {
|
||||
return toJSONError(ctx, errAccessDenied)
|
||||
|
|
|
@ -85,6 +85,18 @@ const (
|
|||
|
||||
// AWSUsername - user friendly name, in MinIO this value is same as your user Access Key.
|
||||
AWSUsername Key = "aws:username"
|
||||
|
||||
// JWTSub - JWT subject claim substitution.
|
||||
JWTSub Key = "jwt:sub"
|
||||
|
||||
// JWTIss issuer claim substitution.
|
||||
JWTIss Key = "jwt:iss"
|
||||
|
||||
// JWTAud audience claim substitution.
|
||||
JWTAud Key = "jwt:aud"
|
||||
|
||||
// JWTJti JWT unique identifier claim substitution.
|
||||
JWTJti Key = "jwt:jti"
|
||||
)
|
||||
|
||||
// AllSupportedKeys - is list of all all supported keys.
|
||||
|
@ -107,6 +119,10 @@ var AllSupportedKeys = []Key{
|
|||
AWSPrincipalType,
|
||||
AWSUserID,
|
||||
AWSUsername,
|
||||
JWTSub,
|
||||
JWTIss,
|
||||
JWTAud,
|
||||
JWTJti,
|
||||
// Add new supported condition keys.
|
||||
}
|
||||
|
||||
|
@ -121,6 +137,10 @@ var CommonKeys = []Key{
|
|||
AWSPrincipalType,
|
||||
AWSUserID,
|
||||
AWSUsername,
|
||||
JWTSub,
|
||||
JWTIss,
|
||||
JWTAud,
|
||||
JWTJti,
|
||||
}
|
||||
|
||||
func substFuncFromValues(values map[string][]string) func(string) string {
|
||||
|
@ -166,8 +186,9 @@ func (key Key) Name() string {
|
|||
|
||||
if strings.HasPrefix(keyString, "aws:") {
|
||||
return strings.TrimPrefix(keyString, "aws:")
|
||||
} else if strings.HasPrefix(keyString, "jwt:") {
|
||||
return strings.TrimPrefix(keyString, "jwt:")
|
||||
}
|
||||
|
||||
return strings.TrimPrefix(keyString, "s3:")
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue