mirror of
https://github.com/minio/minio.git
synced 2024-12-23 21:55:53 -05:00
Add support for federation on browser (#6891)
This commit is contained in:
parent
2aeb3fbe86
commit
d1e41695fe
@ -19,7 +19,6 @@ package cmd
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
@ -627,11 +626,52 @@ type bucketForwardingHandler struct {
|
||||
|
||||
func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if globalDNSConfig == nil || globalDomainName == "" ||
|
||||
guessIsBrowserReq(r) || guessIsHealthCheckReq(r) ||
|
||||
guessIsMetricsReq(r) || guessIsRPCReq(r) || isAdminReq(r) {
|
||||
guessIsHealthCheckReq(r) || guessIsMetricsReq(r) ||
|
||||
guessIsRPCReq(r) || isAdminReq(r) {
|
||||
f.handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// For browser requests, when federation is setup we need to
|
||||
// specifically handle download and upload for browser requests.
|
||||
if guessIsBrowserReq(r) && globalDNSConfig != nil && globalDomainName != "" {
|
||||
var bucket, _ string
|
||||
switch r.Method {
|
||||
case http.MethodPut:
|
||||
if getRequestAuthType(r) == authTypeJWT {
|
||||
bucket, _ = urlPath2BucketObjectName(strings.TrimPrefix(r.URL.Path, minioReservedBucketPath+"/upload"))
|
||||
}
|
||||
case http.MethodGet:
|
||||
if t := r.URL.Query().Get("token"); t != "" {
|
||||
bucket, _ = urlPath2BucketObjectName(strings.TrimPrefix(r.URL.Path, minioReservedBucketPath+"/download"))
|
||||
} else if getRequestAuthType(r) != authTypeJWT && !strings.HasPrefix(r.URL.Path, minioReservedBucketPath) {
|
||||
bucket, _ = urlPath2BucketObjectName(r.URL.Path)
|
||||
}
|
||||
}
|
||||
if bucket != "" {
|
||||
sr, err := globalDNSConfig.Get(bucket)
|
||||
if err != nil {
|
||||
if err == dns.ErrNoEntriesFound {
|
||||
writeErrorResponse(w, ErrNoSuchBucket, r.URL, guessIsBrowserReq(r))
|
||||
} else {
|
||||
writeErrorResponse(w, toAPIErrorCode(context.Background(), err), r.URL, guessIsBrowserReq(r))
|
||||
}
|
||||
return
|
||||
}
|
||||
if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(sr)...)).IsEmpty() {
|
||||
r.URL.Scheme = "http"
|
||||
if globalIsSSL {
|
||||
r.URL.Scheme = "https"
|
||||
}
|
||||
r.URL.Host = getHostFromSrv(sr)
|
||||
f.fwd.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
f.handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
bucket, object := urlPath2BucketObjectName(r.URL.Path)
|
||||
// ListBucket requests should be handled at current endpoint as
|
||||
// all buckets data can be fetched from here.
|
||||
@ -663,12 +703,11 @@ func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(sr)...)).IsEmpty() {
|
||||
host, port := getRandomHostPort(sr)
|
||||
r.URL.Scheme = "http"
|
||||
if globalIsSSL {
|
||||
r.URL.Scheme = "https"
|
||||
}
|
||||
r.URL.Host = fmt.Sprintf("%s:%d", host, port)
|
||||
r.URL.Host = getHostFromSrv(sr)
|
||||
f.fwd.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -183,6 +183,13 @@ func getReqAccessCred(r *http.Request, region string) (cred auth.Credentials) {
|
||||
if cred.AccessKey == "" {
|
||||
cred, _, _ = getReqAccessKeyV2(r)
|
||||
}
|
||||
if cred.AccessKey == "" {
|
||||
claims, owner, _ := webRequestAuthenticate(r)
|
||||
if owner {
|
||||
return globalServerConfig.GetCredential()
|
||||
}
|
||||
cred, _ = globalIAMSys.GetUser(claims.Subject)
|
||||
}
|
||||
return cred
|
||||
}
|
||||
|
||||
@ -194,6 +201,7 @@ func extractReqParams(r *http.Request) map[string]string {
|
||||
|
||||
region := globalServerConfig.GetRegion()
|
||||
cred := getReqAccessCred(r, region)
|
||||
|
||||
// Success.
|
||||
return map[string]string{
|
||||
"region": region,
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"path"
|
||||
"runtime"
|
||||
@ -299,11 +300,11 @@ func getHostsSlice(records []dns.SrvRecord) []string {
|
||||
return hosts
|
||||
}
|
||||
|
||||
// returns a random host (and corresponding port) from a slice of DNS records
|
||||
func getRandomHostPort(records []dns.SrvRecord) (string, int) {
|
||||
// returns a host (and corresponding port) from a slice of DNS records
|
||||
func getHostFromSrv(records []dns.SrvRecord) string {
|
||||
rand.Seed(time.Now().Unix())
|
||||
srvRecord := records[rand.Intn(len(records))]
|
||||
return srvRecord.Host, srvRecord.Port
|
||||
return net.JoinHostPort(srvRecord.Host, fmt.Sprintf("%d", srvRecord.Port))
|
||||
}
|
||||
|
||||
// IsCompressed returns true if the object is marked as compressed.
|
||||
|
@ -668,11 +668,25 @@ func getCpObjMetadataFromHeader(ctx context.Context, r *http.Request, userMeta m
|
||||
|
||||
// Returns a minio-go Client configured to access remote host described by destDNSRecord
|
||||
// Applicable only in a federated deployment
|
||||
var getRemoteInstanceClient = func(r *http.Request, host string, port int) (*miniogo.Core, error) {
|
||||
// In a federated deployment, all the instances share config files and hence expected to have same
|
||||
// credentials, make sure to send the same credentials for which the request came in.
|
||||
var getRemoteInstanceClient = func(r *http.Request, host string) (*miniogo.Core, error) {
|
||||
cred := getReqAccessCred(r, globalServerConfig.GetRegion())
|
||||
return miniogo.NewCore(net.JoinHostPort(host, strconv.Itoa(port)), cred.AccessKey, cred.SecretKey, globalIsSSL)
|
||||
// In a federated deployment, all the instances share config files
|
||||
// and hence expected to have same credentials.
|
||||
core, err := miniogo.NewCore(host, cred.AccessKey, cred.SecretKey, globalIsSSL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
core.SetCustomTransport(NewCustomHTTPTransport())
|
||||
return core, nil
|
||||
}
|
||||
|
||||
// Check if the bucket is on a remote site, this code only gets executed when federation is enabled.
|
||||
var isRemoteCallRequired = func(ctx context.Context, bucket string, objAPI ObjectLayer) bool {
|
||||
if globalDNSConfig == nil {
|
||||
return false
|
||||
}
|
||||
_, err := objAPI.GetBucketInfo(ctx, bucket)
|
||||
return err == toObjectErr(errVolumeNotFound, bucket)
|
||||
}
|
||||
|
||||
// CopyObjectHandler - Copy Object
|
||||
@ -802,17 +816,6 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
srcInfo.metadataOnly = true
|
||||
}
|
||||
|
||||
// Checks if a remote putobject call is needed for CopyObject operation
|
||||
// 1. If source and destination bucket names are same, it means no call needed to etcd to get destination info
|
||||
// 2. If destination bucket doesn't exist locally, only then a etcd call is needed
|
||||
var isRemoteCallRequired = func(ctx context.Context, src, dst string, objAPI ObjectLayer) bool {
|
||||
if src == dst {
|
||||
return false
|
||||
}
|
||||
_, berr := objAPI.GetBucketInfo(ctx, dst)
|
||||
return berr == toObjectErr(errVolumeNotFound, dst)
|
||||
}
|
||||
|
||||
var reader io.Reader
|
||||
var length = srcInfo.Size
|
||||
|
||||
@ -827,10 +830,27 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
length = actualSize
|
||||
}
|
||||
|
||||
// Check if the destination bucket is on a remote site, this code only gets executed
|
||||
// when federation is enabled, ie when globalDNSConfig is non 'nil'.
|
||||
//
|
||||
// This function is similar to isRemoteCallRequired but specifically for COPY object API
|
||||
// if destination and source are same we do not need to check for destnation bucket
|
||||
// to exist locally.
|
||||
var isRemoteCopyRequired = func(ctx context.Context, srcBucket, dstBucket string, objAPI ObjectLayer) bool {
|
||||
if globalDNSConfig == nil {
|
||||
return false
|
||||
}
|
||||
if srcBucket == dstBucket {
|
||||
return false
|
||||
}
|
||||
_, err := objAPI.GetBucketInfo(ctx, dstBucket)
|
||||
return err == toObjectErr(errVolumeNotFound, dstBucket)
|
||||
}
|
||||
|
||||
var compressMetadata map[string]string
|
||||
// No need to compress for remote etcd calls
|
||||
// Pass the decompressed stream to such calls.
|
||||
isCompressed := objectAPI.IsCompressionSupported() && isCompressible(r.Header, srcObject) && !isRemoteCallRequired(ctx, srcBucket, dstBucket, objectAPI)
|
||||
isCompressed := objectAPI.IsCompressionSupported() && isCompressible(r.Header, srcObject) && !isRemoteCopyRequired(ctx, srcBucket, dstBucket, objectAPI)
|
||||
if isCompressed {
|
||||
compressMetadata = make(map[string]string, 2)
|
||||
// Preserving the compression metadata.
|
||||
@ -1009,29 +1029,28 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
|
||||
var objInfo ObjectInfo
|
||||
|
||||
if isRemoteCallRequired(ctx, srcBucket, dstBucket, objectAPI) {
|
||||
if globalDNSConfig == nil {
|
||||
writeErrorResponse(w, ErrNoSuchBucket, r.URL, guessIsBrowserReq(r))
|
||||
if isRemoteCopyRequired(ctx, srcBucket, dstBucket, objectAPI) {
|
||||
var dstRecords []dns.SrvRecord
|
||||
dstRecords, err = globalDNSConfig.Get(dstBucket)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
var dstRecords []dns.SrvRecord
|
||||
if dstRecords, err = globalDNSConfig.Get(dstBucket); err == nil {
|
||||
// Send PutObject request to appropriate instance (in federated deployment)
|
||||
host, port := getRandomHostPort(dstRecords)
|
||||
client, rerr := getRemoteInstanceClient(r, host, port)
|
||||
if rerr != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
remoteObjInfo, rerr := client.PutObject(dstBucket, dstObject, srcInfo.Reader,
|
||||
srcInfo.Size, "", "", srcInfo.UserDefined, dstOpts.ServerSideEncryption)
|
||||
if rerr != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
objInfo.ETag = remoteObjInfo.ETag
|
||||
objInfo.ModTime = remoteObjInfo.LastModified
|
||||
|
||||
// Send PutObject request to appropriate instance (in federated deployment)
|
||||
client, rerr := getRemoteInstanceClient(r, getHostFromSrv(dstRecords))
|
||||
if rerr != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
remoteObjInfo, rerr := client.PutObject(dstBucket, dstObject, srcInfo.Reader,
|
||||
srcInfo.Size, "", "", srcInfo.UserDefined, dstOpts.ServerSideEncryption)
|
||||
if rerr != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(ctx, rerr), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
objInfo.ETag = remoteObjInfo.ETag
|
||||
objInfo.ModTime = remoteObjInfo.LastModified
|
||||
} else {
|
||||
// Copy source object to destination, if source and destination
|
||||
// object is same then only metadata is updated.
|
||||
|
@ -99,11 +99,14 @@ func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, rep
|
||||
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
|
||||
}
|
||||
// if etcd is set disallow changing credentials through UI
|
||||
if globalEtcdClient != nil {
|
||||
reply.MinioGlobalInfo["isEnvCreds"] = true
|
||||
}
|
||||
|
||||
reply.MinioMemory = mem
|
||||
reply.MinioPlatform = platform
|
||||
@ -148,7 +151,7 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep
|
||||
if authErr != nil {
|
||||
return toJSONError(authErr)
|
||||
}
|
||||
// TODO: Allow MakeBucket in future.
|
||||
|
||||
if !owner {
|
||||
return toJSONError(errAccessDenied)
|
||||
}
|
||||
@ -201,11 +204,33 @@ func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs,
|
||||
if authErr != nil {
|
||||
return toJSONError(authErr)
|
||||
}
|
||||
// TODO: Allow DeleteBucket in future.
|
||||
|
||||
if !owner {
|
||||
return toJSONError(errAccessDenied)
|
||||
}
|
||||
|
||||
reply.UIVersion = browser.UIVersion
|
||||
|
||||
if isRemoteCallRequired(context.Background(), args.BucketName, objectAPI) {
|
||||
sr, err := globalDNSConfig.Get(args.BucketName)
|
||||
if err != nil {
|
||||
if err == dns.ErrNoEntriesFound {
|
||||
return toJSONError(BucketNotFound{
|
||||
Bucket: args.BucketName,
|
||||
}, args.BucketName)
|
||||
}
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
core, err := getRemoteInstanceClient(r, getHostFromSrv(sr))
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
if err = core.RemoveBucket(args.BucketName); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
deleteBucket := objectAPI.DeleteBucket
|
||||
@ -229,7 +254,6 @@ func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs,
|
||||
}
|
||||
}
|
||||
|
||||
reply.UIVersion = browser.UIVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -353,6 +377,42 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
||||
listObjects = web.CacheAPI().ListObjects
|
||||
}
|
||||
|
||||
if isRemoteCallRequired(context.Background(), args.BucketName, objectAPI) {
|
||||
sr, err := globalDNSConfig.Get(args.BucketName)
|
||||
if err != nil {
|
||||
if err == dns.ErrNoEntriesFound {
|
||||
return toJSONError(BucketNotFound{
|
||||
Bucket: args.BucketName,
|
||||
}, args.BucketName)
|
||||
}
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
core, err := getRemoteInstanceClient(r, getHostFromSrv(sr))
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
result, err := core.ListObjects(args.BucketName, args.Prefix, args.Marker, slashSeparator, 1000)
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
reply.NextMarker = result.NextMarker
|
||||
reply.IsTruncated = result.IsTruncated
|
||||
for _, obj := range result.Contents {
|
||||
reply.Objects = append(reply.Objects, WebObjectInfo{
|
||||
Key: obj.Key,
|
||||
LastModified: obj.LastModified,
|
||||
Size: obj.Size,
|
||||
ContentType: obj.ContentType,
|
||||
})
|
||||
}
|
||||
for _, p := range result.CommonPrefixes {
|
||||
reply.Objects = append(reply.Objects, WebObjectInfo{
|
||||
Key: p.Prefix,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
claims, owner, authErr := webRequestAuthenticate(r)
|
||||
if authErr != nil {
|
||||
if authErr == errNoAuthToken {
|
||||
@ -493,6 +553,40 @@ func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs,
|
||||
return toJSONError(errInvalidArgument)
|
||||
}
|
||||
|
||||
reply.UIVersion = browser.UIVersion
|
||||
if isRemoteCallRequired(context.Background(), args.BucketName, objectAPI) {
|
||||
sr, err := globalDNSConfig.Get(args.BucketName)
|
||||
if err != nil {
|
||||
if err == dns.ErrNoEntriesFound {
|
||||
return toJSONError(BucketNotFound{
|
||||
Bucket: args.BucketName,
|
||||
}, args.BucketName)
|
||||
}
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
core, err := getRemoteInstanceClient(r, getHostFromSrv(sr))
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
objectsCh := make(chan string)
|
||||
|
||||
// Send object names that are needed to be removed to objectsCh
|
||||
go func() {
|
||||
defer close(objectsCh)
|
||||
|
||||
for _, objectName := range args.Objects {
|
||||
objectsCh <- objectName
|
||||
}
|
||||
}()
|
||||
|
||||
for resp := range core.RemoveObjects(args.BucketName, objectsCh) {
|
||||
if resp.Err != nil {
|
||||
return toJSONError(resp.Err, args.BucketName, resp.ObjectName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
next:
|
||||
for _, objectName := range args.Objects {
|
||||
@ -516,7 +610,7 @@ next:
|
||||
return toJSONError(errAccessDenied)
|
||||
}
|
||||
|
||||
if err = deleteObject(nil, objectAPI, web.CacheAPI(), args.BucketName, objectName, r); err != nil {
|
||||
if err = deleteObject(context.Background(), objectAPI, web.CacheAPI(), args.BucketName, objectName, r); err != nil {
|
||||
break next
|
||||
}
|
||||
continue
|
||||
@ -543,7 +637,7 @@ next:
|
||||
}
|
||||
marker = lo.NextMarker
|
||||
for _, obj := range lo.Objects {
|
||||
err = deleteObject(nil, objectAPI, web.CacheAPI(), args.BucketName, obj.Name, r)
|
||||
err = deleteObject(context.Background(), objectAPI, web.CacheAPI(), args.BucketName, obj.Name, r)
|
||||
if err != nil {
|
||||
break next
|
||||
}
|
||||
@ -559,7 +653,6 @@ next:
|
||||
return toJSONError(err, args.BucketName, "")
|
||||
}
|
||||
|
||||
reply.UIVersion = browser.UIVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -633,8 +726,7 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se
|
||||
}
|
||||
|
||||
// If creds are set through ENV disallow changing credentials.
|
||||
// TODO: Multi-user credentials also cannot be changed from browser.
|
||||
if globalIsEnvCreds || globalWORMEnabled || !owner {
|
||||
if globalIsEnvCreds || globalWORMEnabled || !owner || globalEtcdClient != nil {
|
||||
return toJSONError(errChangeCredNotAllowed)
|
||||
}
|
||||
|
||||
@ -858,6 +950,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
pReader = NewPutObjReader(rawReader, hashReader, objectEncryptionKey)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that metadata does not contain sensitive information
|
||||
crypto.RemoveSensitiveEntries(metadata)
|
||||
|
||||
@ -1306,18 +1399,48 @@ func (web *webAPIHandlers) GetBucketPolicy(r *http.Request, args *GetBucketPolic
|
||||
return toJSONError(errAccessDenied)
|
||||
}
|
||||
|
||||
bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName)
|
||||
if err != nil {
|
||||
if _, ok := err.(BucketPolicyNotFound); !ok {
|
||||
var policyInfo = &miniogopolicy.BucketAccessPolicy{Version: "2012-10-17"}
|
||||
if isRemoteCallRequired(context.Background(), args.BucketName, objectAPI) {
|
||||
sr, err := globalDNSConfig.Get(args.BucketName)
|
||||
if err != nil {
|
||||
if err == dns.ErrNoEntriesFound {
|
||||
return toJSONError(BucketNotFound{
|
||||
Bucket: args.BucketName,
|
||||
}, args.BucketName)
|
||||
}
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
client, rerr := getRemoteInstanceClient(r, getHostFromSrv(sr))
|
||||
if rerr != nil {
|
||||
return toJSONError(rerr, args.BucketName)
|
||||
}
|
||||
policyStr, err := client.GetBucketPolicy(args.BucketName)
|
||||
if err != nil {
|
||||
return toJSONError(rerr, args.BucketName)
|
||||
}
|
||||
bucketPolicy, err := policy.ParseConfig(strings.NewReader(policyStr), args.BucketName)
|
||||
if err != nil {
|
||||
return toJSONError(rerr, args.BucketName)
|
||||
}
|
||||
policyInfo, err = PolicyToBucketAccessPolicy(bucketPolicy)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
} else {
|
||||
bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName)
|
||||
if err != nil {
|
||||
if _, ok := err.(BucketPolicyNotFound); !ok {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
policyInfo, err := PolicyToBucketAccessPolicy(bucketPolicy)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
policyInfo, err = PolicyToBucketAccessPolicy(bucketPolicy)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
}
|
||||
|
||||
reply.UIVersion = browser.UIVersion
|
||||
@ -1359,17 +1482,42 @@ func (web *webAPIHandlers) ListAllBucketPolicies(r *http.Request, args *ListAllB
|
||||
return toJSONError(errAccessDenied)
|
||||
}
|
||||
|
||||
bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName)
|
||||
if err != nil {
|
||||
if _, ok := err.(BucketPolicyNotFound); !ok {
|
||||
var policyInfo = new(miniogopolicy.BucketAccessPolicy)
|
||||
if isRemoteCallRequired(context.Background(), args.BucketName, objectAPI) {
|
||||
sr, err := globalDNSConfig.Get(args.BucketName)
|
||||
if err != nil {
|
||||
if err == dns.ErrNoEntriesFound {
|
||||
return toJSONError(BucketNotFound{
|
||||
Bucket: args.BucketName,
|
||||
}, args.BucketName)
|
||||
}
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
core, rerr := getRemoteInstanceClient(r, getHostFromSrv(sr))
|
||||
if rerr != nil {
|
||||
return toJSONError(rerr, args.BucketName)
|
||||
}
|
||||
var policyStr string
|
||||
policyStr, err = core.Client.GetBucketPolicy(args.BucketName)
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
if policyStr != "" {
|
||||
if err = json.Unmarshal([]byte(policyStr), policyInfo); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bucketPolicy, err := objectAPI.GetBucketPolicy(context.Background(), args.BucketName)
|
||||
if err != nil {
|
||||
if _, ok := err.(BucketPolicyNotFound); !ok {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
}
|
||||
policyInfo, err = PolicyToBucketAccessPolicy(bucketPolicy)
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
}
|
||||
|
||||
policyInfo, err := PolicyToBucketAccessPolicy(bucketPolicy)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
reply.UIVersion = browser.UIVersion
|
||||
@ -1419,43 +1567,94 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
bucketPolicy, err := objectAPI.GetBucketPolicy(ctx, args.BucketName)
|
||||
if err != nil {
|
||||
if _, ok := err.(BucketPolicyNotFound); !ok {
|
||||
if isRemoteCallRequired(context.Background(), args.BucketName, objectAPI) {
|
||||
sr, err := globalDNSConfig.Get(args.BucketName)
|
||||
if err != nil {
|
||||
if err == dns.ErrNoEntriesFound {
|
||||
return toJSONError(BucketNotFound{
|
||||
Bucket: args.BucketName,
|
||||
}, args.BucketName)
|
||||
}
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
}
|
||||
core, rerr := getRemoteInstanceClient(r, getHostFromSrv(sr))
|
||||
if rerr != nil {
|
||||
return toJSONError(rerr, args.BucketName)
|
||||
}
|
||||
var policyStr string
|
||||
// Use the abstracted API instead of core, such that
|
||||
// NoSuchBucketPolicy errors are automatically handled.
|
||||
policyStr, err = core.Client.GetBucketPolicy(args.BucketName)
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
var policyInfo = &miniogopolicy.BucketAccessPolicy{Version: "2012-10-17"}
|
||||
if policyStr != "" {
|
||||
if err = json.Unmarshal([]byte(policyStr), policyInfo); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
}
|
||||
|
||||
policyInfo, err := PolicyToBucketAccessPolicy(bucketPolicy)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
policyInfo.Statements = miniogopolicy.SetPolicy(policyInfo.Statements, policyType, args.BucketName, args.Prefix)
|
||||
if len(policyInfo.Statements) == 0 {
|
||||
if err = core.SetBucketPolicy(args.BucketName, ""); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
policyInfo.Statements = miniogopolicy.SetPolicy(policyInfo.Statements, policyType, args.BucketName, args.Prefix)
|
||||
|
||||
if len(policyInfo.Statements) == 0 {
|
||||
if err = objectAPI.DeleteBucketPolicy(ctx, args.BucketName); err != nil {
|
||||
bucketPolicy, err := BucketAccessPolicyToPolicy(policyInfo)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
globalPolicySys.Remove(args.BucketName)
|
||||
return nil
|
||||
}
|
||||
policyData, err := json.Marshal(bucketPolicy)
|
||||
if err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
bucketPolicy, err = BucketAccessPolicyToPolicy(policyInfo)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
if err = core.SetBucketPolicy(args.BucketName, string(policyData)); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
// Parse validate and save bucket policy.
|
||||
if err := objectAPI.SetBucketPolicy(ctx, args.BucketName, bucketPolicy); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
} else {
|
||||
bucketPolicy, err := objectAPI.GetBucketPolicy(ctx, args.BucketName)
|
||||
if err != nil {
|
||||
if _, ok := err.(BucketPolicyNotFound); !ok {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
}
|
||||
policyInfo, err := PolicyToBucketAccessPolicy(bucketPolicy)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
globalPolicySys.Set(args.BucketName, *bucketPolicy)
|
||||
globalNotificationSys.SetBucketPolicy(ctx, args.BucketName, bucketPolicy)
|
||||
policyInfo.Statements = miniogopolicy.SetPolicy(policyInfo.Statements, policyType, args.BucketName, args.Prefix)
|
||||
if len(policyInfo.Statements) == 0 {
|
||||
if err = objectAPI.DeleteBucketPolicy(ctx, args.BucketName); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
globalPolicySys.Remove(args.BucketName)
|
||||
return nil
|
||||
}
|
||||
|
||||
bucketPolicy, err = BucketAccessPolicyToPolicy(policyInfo)
|
||||
if err != nil {
|
||||
// This should not happen.
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
// Parse validate and save bucket policy.
|
||||
if err := objectAPI.SetBucketPolicy(ctx, args.BucketName, bucketPolicy); err != nil {
|
||||
return toJSONError(err, args.BucketName)
|
||||
}
|
||||
|
||||
globalPolicySys.Set(args.BucketName, *bucketPolicy)
|
||||
globalNotificationSys.SetBucketPolicy(ctx, args.BucketName, bucketPolicy)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ func registerWebRouter(router *mux.Router) error {
|
||||
|
||||
// These methods use short-expiry tokens in the URLs. These tokens may unintentionally
|
||||
// be logged, so a new one must be generated for each request.
|
||||
webBrowserRouter.Methods("GET").Path("/download/{bucket}/{object:.+}").Queries("token", "{token:.*}").HandlerFunc(web.Download)
|
||||
webBrowserRouter.Methods("GET").Path("/download/{bucket}/{object:.+}").Queries("token", "{token:.*}").HandlerFunc(httpTraceHdrs(web.Download))
|
||||
webBrowserRouter.Methods("POST").Path("/zip").Queries("token", "{token:.*}").HandlerFunc(httpTraceHdrs(web.DownloadZip))
|
||||
|
||||
// Add compression for assets.
|
||||
|
Loading…
Reference in New Issue
Block a user