mirror of
				https://github.com/minio/minio.git
				synced 2025-10-29 15:55:00 -04:00 
			
		
		
		
	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:
		
							parent
							
								
									ae3c05aa37
								
							
						
					
					
						commit
						2e81f27d27
					
				| @ -358,7 +358,7 @@ func (a authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} else if aType == authTypeJWT { | ||||
| 		// Validate Authorization header if its valid for JWT request. | ||||
| 		if !isHTTPRequestValid(r) { | ||||
| 		if _, _, authErr := webRequestAuthenticate(r); authErr != nil { | ||||
| 			w.WriteHeader(http.StatusUnauthorized) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| @ -205,11 +205,6 @@ func webTokenAuthenticate(token string) (jwtgo.StandardClaims, bool, error) { | ||||
| 	return claims, owner, nil | ||||
| } | ||||
| 
 | ||||
| func isHTTPRequestValid(req *http.Request) bool { | ||||
| 	_, _, err := webRequestAuthenticate(req) | ||||
| 	return err == nil | ||||
| } | ||||
| 
 | ||||
| // Check if the request is authenticated. | ||||
| // Returns nil if the request is authenticated. errNoAuthToken if token missing. | ||||
| // Returns errAuthentication for all other errors. | ||||
|  | ||||
| @ -82,3 +82,6 @@ var errNoSuchUser = errors.New("Specified user does not exist") | ||||
| 
 | ||||
| // error returned in IAM subsystem when policy doesn't 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") | ||||
|  | ||||
| @ -74,8 +74,9 @@ type ServerInfoRep struct { | ||||
| 
 | ||||
| // ServerInfo - get server info. | ||||
| func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, reply *ServerInfoRep) error { | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	host, err := os.Hostname() | ||||
| 	if err != nil { | ||||
| @ -96,6 +97,14 @@ func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, rep | ||||
| 
 | ||||
| 	reply.MinioVersion = Version | ||||
| 	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.MinioPlatform = platform | ||||
| 	reply.MinioRuntime = goruntime | ||||
| @ -115,8 +124,9 @@ func (web *webAPIHandlers) StorageInfo(r *http.Request, args *AuthArgs, reply *S | ||||
| 	if objectAPI == nil { | ||||
| 		return toJSONError(errServerNotInitialized) | ||||
| 	} | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, _, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	reply.StorageInfo = objectAPI.StorageInfo(context.Background()) | ||||
| 	reply.UIVersion = browser.UIVersion | ||||
| @ -134,8 +144,13 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep | ||||
| 	if objectAPI == nil { | ||||
| 		return toJSONError(errServerNotInitialized) | ||||
| 	} | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	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. | ||||
| @ -182,8 +197,13 @@ func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs, | ||||
| 	if objectAPI == nil { | ||||
| 		return toJSONError(errServerNotInitialized) | ||||
| 	} | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	// TODO: Allow DeleteBucket in future. | ||||
| 	if !owner { | ||||
| 		return toJSONError(errAccessDenied) | ||||
| 	} | ||||
| 
 | ||||
| 	ctx := context.Background() | ||||
| @ -338,7 +358,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r | ||||
| 			if !readable { | ||||
| 				// Error out if anonymous user (non-owner) has no access to download or upload objects | ||||
| 				if !writable { | ||||
| 					return errAuthentication | ||||
| 					return errAccessDenied | ||||
| 				} | ||||
| 				// return empty object list if access is write only | ||||
| 				return nil | ||||
| @ -372,7 +392,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r | ||||
| 		if !readable { | ||||
| 			// Error out if anonymous user (non-owner) has no access to download or upload objects | ||||
| 			if !writable { | ||||
| 				return errAuthentication | ||||
| 				return errAccessDenied | ||||
| 			} | ||||
| 			// return empty object list if access is write only | ||||
| 			return nil | ||||
| @ -435,8 +455,10 @@ func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs, | ||||
| 	if web.CacheAPI() != nil { | ||||
| 		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 { | ||||
| @ -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 { | ||||
| 				break next | ||||
| 			} | ||||
| 			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. | ||||
| 		marker := "" | ||||
| 		for { | ||||
| @ -523,8 +567,12 @@ type GenerateAuthReply struct { | ||||
| } | ||||
| 
 | ||||
| func (web webAPIHandlers) GenerateAuth(r *http.Request, args *WebGenericArgs, reply *GenerateAuthReply) error { | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	if !owner { | ||||
| 		return toJSONError(errAccessDenied) | ||||
| 	} | ||||
| 	cred, err := auth.GetNewCredentials() | ||||
| 	if err != nil { | ||||
| @ -551,12 +599,14 @@ type SetAuthReply struct { | ||||
| 
 | ||||
| // SetAuth - Set accessKey and secretKey credentials. | ||||
| func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *SetAuthReply) error { | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 
 | ||||
| 	// 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) | ||||
| 	} | ||||
| 
 | ||||
| @ -588,7 +638,10 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se | ||||
| 			reply.PeerErrMsgs[host.String()] = err.Error() | ||||
| 		} | ||||
| 	} else { | ||||
| 		reply.Token = newAuthToken() | ||||
| 		reply.Token, err = authenticateWeb(creds.AccessKey, creds.SecretKey) | ||||
| 		if err != nil { | ||||
| 			return toJSONError(err) | ||||
| 		} | ||||
| 		reply.UIVersion = browser.UIVersion | ||||
| 	} | ||||
| 
 | ||||
| @ -604,8 +657,12 @@ type GetAuthReply struct { | ||||
| 
 | ||||
| // GetAuth - return accessKey and secretKey credentials. | ||||
| func (web *webAPIHandlers) GetAuth(r *http.Request, args *WebGenericArgs, reply *GetAuthReply) error { | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	if !owner { | ||||
| 		return toJSONError(errAccessDenied) | ||||
| 	} | ||||
| 	creds := globalServerConfig.GetCredential() | ||||
| 	reply.AccessKey = creds.AccessKey | ||||
| @ -622,11 +679,19 @@ type URLTokenReply struct { | ||||
| 
 | ||||
| // CreateURLToken creates a URL token (short-lived) for GET requests. | ||||
| func (web *webAPIHandlers) CreateURLToken(r *http.Request, args *WebGenericArgs, reply *URLTokenReply) error { | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	claims, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 
 | ||||
| 	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) | ||||
| 	if err != nil { | ||||
| @ -1211,8 +1276,12 @@ func (web *webAPIHandlers) GetBucketPolicy(r *http.Request, args *GetBucketPolic | ||||
| 		return toJSONError(errServerNotInitialized) | ||||
| 	} | ||||
| 
 | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	if !owner { | ||||
| 		return toJSONError(errAccessDenied) | ||||
| 	} | ||||
| 
 | ||||
| 	bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName) | ||||
| @ -1260,8 +1329,12 @@ func (web *webAPIHandlers) ListAllBucketPolicies(r *http.Request, args *ListAllB | ||||
| 		return toJSONError(errServerNotInitialized) | ||||
| 	} | ||||
| 
 | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	if !owner { | ||||
| 		return toJSONError(errAccessDenied) | ||||
| 	} | ||||
| 
 | ||||
| 	bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName) | ||||
| @ -1307,8 +1380,12 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic | ||||
| 		return toJSONError(errServerNotInitialized) | ||||
| 	} | ||||
| 
 | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	_, owner, authErr := webRequestAuthenticate(r) | ||||
| 	if authErr != nil { | ||||
| 		return toJSONError(authErr) | ||||
| 	} | ||||
| 	if !owner { | ||||
| 		return toJSONError(errAccessDenied) | ||||
| 	} | ||||
| 
 | ||||
| 	policyType := miniogopolicy.BucketPolicy(args.Policy) | ||||
| @ -1385,27 +1462,37 @@ type PresignedGetRep struct { | ||||
| 
 | ||||
| // PresignedGET - returns presigned-Get url. | ||||
| func (web *webAPIHandlers) PresignedGet(r *http.Request, args *PresignedGetArgs, reply *PresignedGetRep) error { | ||||
| 	if !isHTTPRequestValid(r) { | ||||
| 		return toJSONError(errAuthentication) | ||||
| 	claims, owner, authErr := webRequestAuthenticate(r) | ||||
| 	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 == "" { | ||||
| 		return &json2.Error{ | ||||
| 			Message: "Bucket and Object are mandatory arguments.", | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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 | ||||
| } | ||||
| 
 | ||||
| // Returns presigned url for GET method. | ||||
| func presignedGet(host, bucket, object string, expiry int64) string { | ||||
| 	cred := globalServerConfig.GetCredential() | ||||
| 	region := globalServerConfig.GetRegion() | ||||
| 
 | ||||
| 	accessKey := cred.AccessKey | ||||
| 	secretKey := cred.SecretKey | ||||
| func presignedGet(host, bucket, object string, expiry int64, creds auth.Credentials, region string) string { | ||||
| 	accessKey := creds.AccessKey | ||||
| 	secretKey := creds.SecretKey | ||||
| 
 | ||||
| 	date := UTCNow() | ||||
| 	dateStr := date.Format(iso8601Format) | ||||
|  | ||||
| @ -343,7 +343,7 @@ func testDeleteBucketWebHandler(obj ObjectLayer, instanceType string, t TestErrH | ||||
| 		{"minio", false, token, "specified bucket minio does not exist"}, | ||||
| 		{bucketName, false, token, ""}, | ||||
| 		{bucketName, true, token, "Bucket not empty"}, | ||||
| 		{bucketName, false, "", "Authentication failed"}, | ||||
| 		{bucketName, false, "", "JWT token missing"}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, test := range testCases { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user