mirror of
https://github.com/minio/minio.git
synced 2024-12-23 21:55:53 -05: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…
Reference in New Issue
Block a user