Allow all browser calls to honor multi-users (#6645)

- GetAuth
- SetAuth
- GenerateAuth

Disallow changing bucket level metadata or creating/deleting buckets.
This commit is contained in:
Harshavardhana 2018-10-17 16:23:09 -07:00 committed by kannappanr
parent ae3c05aa37
commit 2e81f27d27
5 changed files with 129 additions and 44 deletions

View File

@ -358,7 +358,7 @@ func (a authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} else if aType == authTypeJWT { } else if aType == authTypeJWT {
// Validate Authorization header if its valid for JWT request. // Validate Authorization header if its valid for JWT request.
if !isHTTPRequestValid(r) { if _, _, authErr := webRequestAuthenticate(r); authErr != nil {
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
return return
} }

View File

@ -205,11 +205,6 @@ func webTokenAuthenticate(token string) (jwtgo.StandardClaims, bool, error) {
return claims, owner, nil return claims, owner, nil
} }
func isHTTPRequestValid(req *http.Request) bool {
_, _, err := webRequestAuthenticate(req)
return err == nil
}
// Check if the request is authenticated. // Check if the request is authenticated.
// Returns nil if the request is authenticated. errNoAuthToken if token missing. // Returns nil if the request is authenticated. errNoAuthToken if token missing.
// Returns errAuthentication for all other errors. // Returns errAuthentication for all other errors.

View File

@ -82,3 +82,6 @@ var errNoSuchUser = errors.New("Specified user does not exist")
// error returned in IAM subsystem when policy doesn't exist. // error returned in IAM subsystem when policy doesn't exist.
var errNoSuchPolicy = errors.New("Specified canned policy does not exist") var errNoSuchPolicy = errors.New("Specified canned policy does not exist")
// error returned when access is denied.
var errAccessDenied = errors.New("Do not have enough permissions to access this resource")

View File

@ -74,8 +74,9 @@ type ServerInfoRep struct {
// ServerInfo - get server info. // ServerInfo - get server info.
func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, reply *ServerInfoRep) error { func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, reply *ServerInfoRep) error {
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
} }
host, err := os.Hostname() host, err := os.Hostname()
if err != nil { if err != nil {
@ -96,6 +97,14 @@ func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, rep
reply.MinioVersion = Version reply.MinioVersion = Version
reply.MinioGlobalInfo = getGlobalInfo() reply.MinioGlobalInfo = getGlobalInfo()
// If ENV creds are not set and incoming user is not owner
// disable changing credentials.
// TODO: fix this in future and allow changing user credentials.
v, ok := reply.MinioGlobalInfo["isEnvCreds"].(bool)
if ok && !v {
reply.MinioGlobalInfo["isEnvCreds"] = !owner
}
reply.MinioMemory = mem reply.MinioMemory = mem
reply.MinioPlatform = platform reply.MinioPlatform = platform
reply.MinioRuntime = goruntime reply.MinioRuntime = goruntime
@ -115,8 +124,9 @@ func (web *webAPIHandlers) StorageInfo(r *http.Request, args *AuthArgs, reply *S
if objectAPI == nil { if objectAPI == nil {
return toJSONError(errServerNotInitialized) return toJSONError(errServerNotInitialized)
} }
if !isHTTPRequestValid(r) { _, _, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
} }
reply.StorageInfo = objectAPI.StorageInfo(context.Background()) reply.StorageInfo = objectAPI.StorageInfo(context.Background())
reply.UIVersion = browser.UIVersion reply.UIVersion = browser.UIVersion
@ -134,8 +144,13 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep
if objectAPI == nil { if objectAPI == nil {
return toJSONError(errServerNotInitialized) return toJSONError(errServerNotInitialized)
} }
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
// TODO: Allow MakeBucket in future.
if !owner {
return toJSONError(errAccessDenied)
} }
// Check if bucket is a reserved bucket name or invalid. // Check if bucket is a reserved bucket name or invalid.
@ -182,8 +197,13 @@ func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs,
if objectAPI == nil { if objectAPI == nil {
return toJSONError(errServerNotInitialized) return toJSONError(errServerNotInitialized)
} }
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
// TODO: Allow DeleteBucket in future.
if !owner {
return toJSONError(errAccessDenied)
} }
ctx := context.Background() ctx := context.Background()
@ -338,7 +358,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
if !readable { if !readable {
// Error out if anonymous user (non-owner) has no access to download or upload objects // Error out if anonymous user (non-owner) has no access to download or upload objects
if !writable { if !writable {
return errAuthentication return errAccessDenied
} }
// return empty object list if access is write only // return empty object list if access is write only
return nil return nil
@ -372,7 +392,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
if !readable { if !readable {
// Error out if anonymous user (non-owner) has no access to download or upload objects // Error out if anonymous user (non-owner) has no access to download or upload objects
if !writable { if !writable {
return errAuthentication return errAccessDenied
} }
// return empty object list if access is write only // return empty object list if access is write only
return nil return nil
@ -435,8 +455,10 @@ func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs,
if web.CacheAPI() != nil { if web.CacheAPI() != nil {
listObjects = web.CacheAPI().ListObjects listObjects = web.CacheAPI().ListObjects
} }
if !isHTTPRequestValid(r) {
return toJSONError(errAuthentication) claims, owner, authErr := webRequestAuthenticate(r)
if authErr != nil {
return toJSONError(authErr)
} }
if args.BucketName == "" || len(args.Objects) == 0 { if args.BucketName == "" || len(args.Objects) == 0 {
@ -455,12 +477,34 @@ next:
} }
} }
if !globalIAMSys.IsAllowed(iampolicy.Args{
AccountName: claims.Subject,
Action: iampolicy.Action(policy.DeleteObjectAction),
BucketName: args.BucketName,
ConditionValues: getConditionValues(r, ""),
IsOwner: owner,
ObjectName: objectName,
}) {
return toJSONError(errAccessDenied)
}
if err = deleteObject(nil, objectAPI, web.CacheAPI(), args.BucketName, objectName, r); err != nil { if err = deleteObject(nil, objectAPI, web.CacheAPI(), args.BucketName, objectName, r); err != nil {
break next break next
} }
continue continue
} }
if !globalIAMSys.IsAllowed(iampolicy.Args{
AccountName: claims.Subject,
Action: iampolicy.Action(policy.DeleteObjectAction),
BucketName: args.BucketName,
ConditionValues: getConditionValues(r, ""),
IsOwner: owner,
ObjectName: objectName,
}) {
return toJSONError(errAccessDenied)
}
// For directories, list the contents recursively and remove. // For directories, list the contents recursively and remove.
marker := "" marker := ""
for { for {
@ -523,8 +567,12 @@ type GenerateAuthReply struct {
} }
func (web webAPIHandlers) GenerateAuth(r *http.Request, args *WebGenericArgs, reply *GenerateAuthReply) error { func (web webAPIHandlers) GenerateAuth(r *http.Request, args *WebGenericArgs, reply *GenerateAuthReply) error {
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
if !owner {
return toJSONError(errAccessDenied)
} }
cred, err := auth.GetNewCredentials() cred, err := auth.GetNewCredentials()
if err != nil { if err != nil {
@ -551,12 +599,14 @@ type SetAuthReply struct {
// SetAuth - Set accessKey and secretKey credentials. // SetAuth - Set accessKey and secretKey credentials.
func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *SetAuthReply) error { func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *SetAuthReply) error {
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
} }
// If creds are set through ENV disallow changing credentials. // If creds are set through ENV disallow changing credentials.
if globalIsEnvCreds || globalWORMEnabled { // TODO: Multi-user credentials also cannot be changed from browser.
if globalIsEnvCreds || globalWORMEnabled || !owner {
return toJSONError(errChangeCredNotAllowed) return toJSONError(errChangeCredNotAllowed)
} }
@ -588,7 +638,10 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se
reply.PeerErrMsgs[host.String()] = err.Error() reply.PeerErrMsgs[host.String()] = err.Error()
} }
} else { } else {
reply.Token = newAuthToken() reply.Token, err = authenticateWeb(creds.AccessKey, creds.SecretKey)
if err != nil {
return toJSONError(err)
}
reply.UIVersion = browser.UIVersion reply.UIVersion = browser.UIVersion
} }
@ -604,8 +657,12 @@ type GetAuthReply struct {
// GetAuth - return accessKey and secretKey credentials. // GetAuth - return accessKey and secretKey credentials.
func (web *webAPIHandlers) GetAuth(r *http.Request, args *WebGenericArgs, reply *GetAuthReply) error { func (web *webAPIHandlers) GetAuth(r *http.Request, args *WebGenericArgs, reply *GetAuthReply) error {
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
if !owner {
return toJSONError(errAccessDenied)
} }
creds := globalServerConfig.GetCredential() creds := globalServerConfig.GetCredential()
reply.AccessKey = creds.AccessKey reply.AccessKey = creds.AccessKey
@ -622,11 +679,19 @@ type URLTokenReply struct {
// CreateURLToken creates a URL token (short-lived) for GET requests. // CreateURLToken creates a URL token (short-lived) for GET requests.
func (web *webAPIHandlers) CreateURLToken(r *http.Request, args *WebGenericArgs, reply *URLTokenReply) error { func (web *webAPIHandlers) CreateURLToken(r *http.Request, args *WebGenericArgs, reply *URLTokenReply) error {
if !isHTTPRequestValid(r) { claims, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
} }
creds := globalServerConfig.GetCredential() creds := globalServerConfig.GetCredential()
if !owner {
var ok bool
creds, ok = globalIAMSys.GetUser(claims.Subject)
if !ok {
return toJSONError(errInvalidAccessKeyID)
}
}
token, err := authenticateURL(creds.AccessKey, creds.SecretKey) token, err := authenticateURL(creds.AccessKey, creds.SecretKey)
if err != nil { if err != nil {
@ -1211,8 +1276,12 @@ func (web *webAPIHandlers) GetBucketPolicy(r *http.Request, args *GetBucketPolic
return toJSONError(errServerNotInitialized) return toJSONError(errServerNotInitialized)
} }
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
if !owner {
return toJSONError(errAccessDenied)
} }
bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName) bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName)
@ -1260,8 +1329,12 @@ func (web *webAPIHandlers) ListAllBucketPolicies(r *http.Request, args *ListAllB
return toJSONError(errServerNotInitialized) return toJSONError(errServerNotInitialized)
} }
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
if !owner {
return toJSONError(errAccessDenied)
} }
bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName) bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName)
@ -1307,8 +1380,12 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
return toJSONError(errServerNotInitialized) return toJSONError(errServerNotInitialized)
} }
if !isHTTPRequestValid(r) { _, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
if !owner {
return toJSONError(errAccessDenied)
} }
policyType := miniogopolicy.BucketPolicy(args.Policy) policyType := miniogopolicy.BucketPolicy(args.Policy)
@ -1385,27 +1462,37 @@ type PresignedGetRep struct {
// PresignedGET - returns presigned-Get url. // PresignedGET - returns presigned-Get url.
func (web *webAPIHandlers) PresignedGet(r *http.Request, args *PresignedGetArgs, reply *PresignedGetRep) error { func (web *webAPIHandlers) PresignedGet(r *http.Request, args *PresignedGetArgs, reply *PresignedGetRep) error {
if !isHTTPRequestValid(r) { claims, owner, authErr := webRequestAuthenticate(r)
return toJSONError(errAuthentication) if authErr != nil {
return toJSONError(authErr)
}
var creds auth.Credentials
if !owner {
var ok bool
creds, ok = globalIAMSys.GetUser(claims.Subject)
if !ok {
return toJSONError(errInvalidAccessKeyID)
}
} else {
creds = globalServerConfig.GetCredential()
} }
region := globalServerConfig.GetRegion()
if args.BucketName == "" || args.ObjectName == "" { if args.BucketName == "" || args.ObjectName == "" {
return &json2.Error{ return &json2.Error{
Message: "Bucket and Object are mandatory arguments.", Message: "Bucket and Object are mandatory arguments.",
} }
} }
reply.UIVersion = browser.UIVersion reply.UIVersion = browser.UIVersion
reply.URL = presignedGet(args.HostName, args.BucketName, args.ObjectName, args.Expiry) reply.URL = presignedGet(args.HostName, args.BucketName, args.ObjectName, args.Expiry, creds, region)
return nil return nil
} }
// Returns presigned url for GET method. // Returns presigned url for GET method.
func presignedGet(host, bucket, object string, expiry int64) string { func presignedGet(host, bucket, object string, expiry int64, creds auth.Credentials, region string) string {
cred := globalServerConfig.GetCredential() accessKey := creds.AccessKey
region := globalServerConfig.GetRegion() secretKey := creds.SecretKey
accessKey := cred.AccessKey
secretKey := cred.SecretKey
date := UTCNow() date := UTCNow()
dateStr := date.Format(iso8601Format) dateStr := date.Format(iso8601Format)

View File

@ -343,7 +343,7 @@ func testDeleteBucketWebHandler(obj ObjectLayer, instanceType string, t TestErrH
{"minio", false, token, "specified bucket minio does not exist"}, {"minio", false, token, "specified bucket minio does not exist"},
{bucketName, false, token, ""}, {bucketName, false, token, ""},
{bucketName, true, token, "Bucket not empty"}, {bucketName, true, token, "Bucket not empty"},
{bucketName, false, "", "Authentication failed"}, {bucketName, false, "", "JWT token missing"},
} }
for _, test := range testCases { for _, test := range testCases {