package s3acl

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/seaweedfs/seaweedfs/weed/filer"
	"github.com/seaweedfs/seaweedfs/weed/glog"
	"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
	"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
	"github.com/seaweedfs/seaweedfs/weed/s3api/s3account"
	"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
	"github.com/seaweedfs/seaweedfs/weed/util"
	"net/http"
	"strings"
)

var customAclHeaders = []string{s3_constants.AmzAclFullControl, s3_constants.AmzAclRead, s3_constants.AmzAclReadAcp, s3_constants.AmzAclWrite, s3_constants.AmzAclWriteAcp}

// GetAccountId get AccountId from request headers, AccountAnonymousId will be return if not presen
func GetAccountId(r *http.Request) string {
	id := r.Header.Get(s3_constants.AmzAccountId)
	if len(id) == 0 {
		return s3account.AccountAnonymous.Id
	} else {
		return id
	}
}

// ValidateAccount validate weather request account id is allowed to access
func ValidateAccount(requestAccountId string, allowedAccounts ...string) bool {
	for _, allowedAccount := range allowedAccounts {
		if requestAccountId == allowedAccount {
			return true
		}
	}
	return false
}

// ExtractBucketAcl extracts the acl from the request body, or from the header if request body is empty
func ExtractBucketAcl(r *http.Request, accountManager *s3account.AccountManager, objectOwnership, bucketOwnerId, requestAccountId string, createBucket bool) (grants []*s3.Grant, errCode s3err.ErrorCode) {
	cannedAclPresent := false
	if r.Header.Get(s3_constants.AmzCannedAcl) != "" {
		cannedAclPresent = true
	}
	customAclPresent := false
	for _, customAclHeader := range customAclHeaders {
		if r.Header.Get(customAclHeader) != "" {
			customAclPresent = true
			break
		}
	}

	// AccessControlList body is not support when create object/bucket
	if !createBucket && r.Body != nil && r.Body != http.NoBody {
		defer util.CloseRequest(r)
		if cannedAclPresent || customAclPresent {
			return nil, s3err.ErrUnexpectedContent
		}
		var acp s3.AccessControlPolicy
		err := xmlutil.UnmarshalXML(&acp, xml.NewDecoder(r.Body), "")
		if err != nil || acp.Owner == nil || acp.Owner.ID == nil {
			return nil, s3err.ErrInvalidRequest
		}

		//owner should present && owner is immutable
		if *acp.Owner.ID == "" || *acp.Owner.ID != bucketOwnerId {
			glog.V(3).Infof("set acl denied! owner account is not consistent, request account id: %s, expect account id: %s", *acp.Owner.ID, bucketOwnerId)
			return nil, s3err.ErrAccessDenied
		}
		grants = acp.Grants
	} else {
		if cannedAclPresent && customAclPresent {
			return nil, s3err.ErrInvalidRequest
		}
		if cannedAclPresent {
			grants, errCode = ExtractBucketCannedAcl(r, requestAccountId)
		} else if customAclPresent {
			grants, errCode = ExtractCustomAcl(r)
		}
		if errCode != s3err.ErrNone {
			return nil, errCode
		}
	}
	errCode = ValidateObjectOwnershipAndGrants(objectOwnership, bucketOwnerId, grants)
	if errCode != s3err.ErrNone {
		return nil, errCode
	}
	grants, errCode = ValidateAndTransferGrants(accountManager, grants)
	if errCode != s3err.ErrNone {
		return nil, errCode
	}
	return grants, s3err.ErrNone
}

// ExtractObjectAcl extracts the acl from the request body, or from the header if request body is empty
func ExtractObjectAcl(r *http.Request, accountManager *s3account.AccountManager, objectOwnership, bucketOwnerId, requestAccountId string, createObject bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) {
	cannedAclPresent := false
	if r.Header.Get(s3_constants.AmzCannedAcl) != "" {
		cannedAclPresent = true
	}
	customAclPresent := false
	for _, customAclHeader := range customAclHeaders {
		if r.Header.Get(customAclHeader) != "" {
			customAclPresent = true
			break
		}
	}

	// AccessControlList body is not support when create object/bucket
	if !createObject && r.Body != nil && r.Body != http.NoBody {
		defer util.CloseRequest(r)
		if cannedAclPresent || customAclPresent {
			return "", nil, s3err.ErrUnexpectedContent
		}
		var acp s3.AccessControlPolicy
		err := xmlutil.UnmarshalXML(&acp, xml.NewDecoder(r.Body), "")
		if err != nil || acp.Owner == nil || acp.Owner.ID == nil {
			return "", nil, s3err.ErrInvalidRequest
		}

		//owner should present && owner is immutable
		if *acp.Owner.ID == "" {
			glog.V(1).Infof("Access denied! The owner id is required when specifying grants using AccessControlList")
			return "", nil, s3err.ErrAccessDenied
		}
		ownerId = *acp.Owner.ID
		grants = acp.Grants
	} else {
		if cannedAclPresent && customAclPresent {
			return "", nil, s3err.ErrInvalidRequest
		}
		if cannedAclPresent {
			ownerId, grants, errCode = ExtractObjectCannedAcl(r, objectOwnership, bucketOwnerId, requestAccountId, createObject)
		} else {
			grants, errCode = ExtractCustomAcl(r)
		}
		if errCode != s3err.ErrNone {
			return "", nil, errCode
		}
	}
	errCode = ValidateObjectOwnershipAndGrants(objectOwnership, bucketOwnerId, grants)
	if errCode != s3err.ErrNone {
		return "", nil, errCode
	}
	grants, errCode = ValidateAndTransferGrants(accountManager, grants)
	return ownerId, grants, errCode
}

func ExtractCustomAcl(r *http.Request) ([]*s3.Grant, s3err.ErrorCode) {
	var errCode s3err.ErrorCode
	var grants []*s3.Grant
	for _, customAclHeader := range customAclHeaders {
		headerValue := r.Header.Get(customAclHeader)
		switch customAclHeader {
		case s3_constants.AmzAclRead:
			errCode = ParseCustomAclHeader(headerValue, s3_constants.PermissionRead, &grants)
		case s3_constants.AmzAclWrite:
			errCode = ParseCustomAclHeader(headerValue, s3_constants.PermissionWrite, &grants)
		case s3_constants.AmzAclReadAcp:
			errCode = ParseCustomAclHeader(headerValue, s3_constants.PermissionReadAcp, &grants)
		case s3_constants.AmzAclWriteAcp:
			errCode = ParseCustomAclHeader(headerValue, s3_constants.PermissionWriteAcp, &grants)
		case s3_constants.AmzAclFullControl:
			errCode = ParseCustomAclHeader(headerValue, s3_constants.PermissionFullControl, &grants)
		default:
			errCode = s3err.ErrInvalidAclArgument
		}
		if errCode != s3err.ErrNone {
			return nil, errCode
		}
	}
	return grants, s3err.ErrNone
}

func ParseCustomAclHeader(headerValue, permission string, grants *[]*s3.Grant) s3err.ErrorCode {
	if len(headerValue) > 0 {
		split := strings.Split(headerValue, ",")
		for _, grantStr := range split {
			kv := strings.Split(grantStr, "=")
			if len(kv) != 2 {
				return s3err.ErrInvalidRequest
			}

			switch strings.TrimSpace(kv[0]) {
			case "id":
				accountId := decodeGranteeValue(kv[1])
				*grants = append(*grants, &s3.Grant{
					Grantee: &s3.Grantee{
						Type: &s3_constants.GrantTypeCanonicalUser,
						ID:   &accountId,
					},
					Permission: &permission,
				})
			case "emailAddress":
				emailAddress := decodeGranteeValue(kv[1])
				*grants = append(*grants, &s3.Grant{
					Grantee: &s3.Grantee{
						Type:         &s3_constants.GrantTypeAmazonCustomerByEmail,
						EmailAddress: &emailAddress,
					},
					Permission: &permission,
				})
			case "uri":
				var groupName string
				groupName = decodeGranteeValue(kv[1])
				*grants = append(*grants, &s3.Grant{
					Grantee: &s3.Grantee{
						Type: &s3_constants.GrantTypeGroup,
						URI:  &groupName,
					},
					Permission: &permission,
				})
			}
		}
	}
	return s3err.ErrNone
}

func decodeGranteeValue(value string) (result string) {
	if !strings.HasPrefix(value, "\"") {
		return value
	}
	_ = json.Unmarshal([]byte(value), &result)
	if result == "" {
		result = value
	}
	return result
}

// ExtractBucketCannedAcl parse bucket canned acl, includes: 'private'|'public-read'|'public-read-write'|'authenticated-read'
func ExtractBucketCannedAcl(request *http.Request, requestAccountId string) (grants []*s3.Grant, err s3err.ErrorCode) {
	cannedAcl := request.Header.Get(s3_constants.AmzCannedAcl)
	if cannedAcl == "" {
		return grants, s3err.ErrNone
	}
	err = s3err.ErrNone
	objectWriterFullControl := &s3.Grant{
		Grantee: &s3.Grantee{
			ID:   &requestAccountId,
			Type: &s3_constants.GrantTypeCanonicalUser,
		},
		Permission: &s3_constants.PermissionFullControl,
	}
	switch cannedAcl {
	case s3_constants.CannedAclPrivate:
		grants = append(grants, objectWriterFullControl)
	case s3_constants.CannedAclPublicRead:
		grants = append(grants, objectWriterFullControl)
		grants = append(grants, s3_constants.PublicRead...)
	case s3_constants.CannedAclPublicReadWrite:
		grants = append(grants, objectWriterFullControl)
		grants = append(grants, s3_constants.PublicReadWrite...)
	case s3_constants.CannedAclAuthenticatedRead:
		grants = append(grants, objectWriterFullControl)
		grants = append(grants, s3_constants.AuthenticatedRead...)
	default:
		err = s3err.ErrInvalidAclArgument
	}
	return
}

// ExtractObjectCannedAcl parse object canned acl, includes: 'private'|'public-read'|'public-read-write'|'authenticated-read'|'aws-exec-read'|'bucket-owner-read'|'bucket-owner-full-control'
func ExtractObjectCannedAcl(request *http.Request, objectOwnership, bucketOwnerId, requestAccountId string, createObject bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) {
	if createObject {
		ownerId = requestAccountId
	}

	cannedAcl := request.Header.Get(s3_constants.AmzCannedAcl)
	if cannedAcl == "" {
		return ownerId, grants, s3err.ErrNone
	}

	errCode = s3err.ErrNone
	objectWriterFullControl := &s3.Grant{
		Grantee: &s3.Grantee{
			ID:   &requestAccountId,
			Type: &s3_constants.GrantTypeCanonicalUser,
		},
		Permission: &s3_constants.PermissionFullControl,
	}

	switch cannedAcl {
	case s3_constants.CannedAclPrivate:
		grants = append(grants, objectWriterFullControl)
	case s3_constants.CannedAclPublicRead:
		grants = append(grants, objectWriterFullControl)
		grants = append(grants, s3_constants.PublicRead...)
	case s3_constants.CannedAclPublicReadWrite:
		grants = append(grants, objectWriterFullControl)
		grants = append(grants, s3_constants.PublicReadWrite...)
	case s3_constants.CannedAclAuthenticatedRead:
		grants = append(grants, objectWriterFullControl)
		grants = append(grants, s3_constants.AuthenticatedRead...)
	case s3_constants.CannedAclLogDeliveryWrite:
		grants = append(grants, objectWriterFullControl)
		grants = append(grants, s3_constants.LogDeliveryWrite...)
	case s3_constants.CannedAclBucketOwnerRead:
		grants = append(grants, objectWriterFullControl)
		if requestAccountId != bucketOwnerId {
			grants = append(grants,
				&s3.Grant{
					Grantee: &s3.Grantee{
						Type: &s3_constants.GrantTypeCanonicalUser,
						ID:   &bucketOwnerId,
					},
					Permission: &s3_constants.PermissionRead,
				})
		}
	case s3_constants.CannedAclBucketOwnerFullControl:
		if bucketOwnerId != "" {
			// if set ownership to 'BucketOwnerPreferred' when upload object, the bucket owner will be the object owner
			if createObject && objectOwnership == s3_constants.OwnershipBucketOwnerPreferred {
				ownerId = bucketOwnerId
				grants = append(grants,
					&s3.Grant{
						Grantee: &s3.Grantee{
							Type: &s3_constants.GrantTypeCanonicalUser,
							ID:   &bucketOwnerId,
						},
						Permission: &s3_constants.PermissionFullControl,
					})
			} else {
				grants = append(grants, objectWriterFullControl)
				if requestAccountId != bucketOwnerId {
					grants = append(grants,
						&s3.Grant{
							Grantee: &s3.Grantee{
								Type: &s3_constants.GrantTypeCanonicalUser,
								ID:   &bucketOwnerId,
							},
							Permission: &s3_constants.PermissionFullControl,
						})
				}
			}
		}
	default:
		errCode = s3err.ErrInvalidAclArgument
	}
	return
}

// ValidateAndTransferGrants validate grant entity exists and transfer Email-Grant to Id-Grant
func ValidateAndTransferGrants(accountManager *s3account.AccountManager, grants []*s3.Grant) ([]*s3.Grant, s3err.ErrorCode) {
	var result []*s3.Grant
	for _, grant := range grants {
		grantee := grant.Grantee
		if grantee == nil || grantee.Type == nil {
			glog.Warning("invalid grantee! grantee or granteeType is nil")
			return nil, s3err.ErrInvalidRequest
		}

		switch *grantee.Type {
		case s3_constants.GrantTypeGroup:
			if grantee.URI == nil {
				glog.Warning("invalid group grantee! group URI is nil")
				return nil, s3err.ErrInvalidRequest
			}
			ok := s3_constants.ValidateGroup(*grantee.URI)
			if !ok {
				glog.Warningf("invalid group grantee! group name[%s] is not valid", *grantee.URI)
				return nil, s3err.ErrInvalidRequest
			}
			result = append(result, grant)
		case s3_constants.GrantTypeCanonicalUser:
			if grantee.ID == nil {
				glog.Warning("invalid canonical grantee! account id is nil")
				return nil, s3err.ErrInvalidRequest
			}
			_, ok := accountManager.IdNameMapping[*grantee.ID]
			if !ok {
				glog.Warningf("invalid canonical grantee! account id[%s] is not exists", *grantee.ID)
				return nil, s3err.ErrInvalidRequest
			}
			result = append(result, grant)
		case s3_constants.GrantTypeAmazonCustomerByEmail:
			if grantee.EmailAddress == nil {
				glog.Warning("invalid email grantee! email address is nil")
				return nil, s3err.ErrInvalidRequest
			}
			accountId, ok := accountManager.EmailIdMapping[*grantee.EmailAddress]
			if !ok {
				glog.Warningf("invalid email grantee! email address[%s] is not exists", *grantee.EmailAddress)
				return nil, s3err.ErrInvalidRequest
			}
			result = append(result, &s3.Grant{
				Grantee: &s3.Grantee{
					Type: &s3_constants.GrantTypeCanonicalUser,
					ID:   &accountId,
				},
				Permission: grant.Permission,
			})
		default:
			return nil, s3err.ErrInvalidRequest
		}
	}
	return result, s3err.ErrNone
}

// ValidateObjectOwnershipAndGrants validate if grants equals OwnerFullControl when 'ObjectOwnership' is 'BucketOwnerEnforced'
func ValidateObjectOwnershipAndGrants(objectOwnership, bucketOwnerId string, grants []*s3.Grant) s3err.ErrorCode {
	if len(grants) == 0 {
		return s3err.ErrNone
	}
	if objectOwnership == "" {
		objectOwnership = s3_constants.DefaultObjectOwnership
	}
	if objectOwnership != s3_constants.OwnershipBucketOwnerEnforced {
		return s3err.ErrNone
	}
	if len(grants) > 1 {
		return s3err.AccessControlListNotSupported
	}

	bucketOwnerFullControlGrant := &s3.Grant{
		Permission: &s3_constants.PermissionFullControl,
		Grantee: &s3.Grantee{
			Type: &s3_constants.GrantTypeCanonicalUser,
			ID:   &bucketOwnerId,
		},
	}
	if GrantEquals(bucketOwnerFullControlGrant, grants[0]) {
		return s3err.ErrNone
	}
	return s3err.AccessControlListNotSupported
}

// DetermineRequiredGrants generates the grant set (Grants) according to accountId and reqPermission.
func DetermineRequiredGrants(accountId, permission string) (grants []*s3.Grant) {
	// group grantee (AllUsers)
	grants = append(grants, &s3.Grant{
		Grantee: &s3.Grantee{
			Type: &s3_constants.GrantTypeGroup,
			URI:  &s3_constants.GranteeGroupAllUsers,
		},
		Permission: &permission,
	})
	grants = append(grants, &s3.Grant{
		Grantee: &s3.Grantee{
			Type: &s3_constants.GrantTypeGroup,
			URI:  &s3_constants.GranteeGroupAllUsers,
		},
		Permission: &s3_constants.PermissionFullControl,
	})

	// canonical grantee (accountId)
	grants = append(grants, &s3.Grant{
		Grantee: &s3.Grantee{
			Type: &s3_constants.GrantTypeCanonicalUser,
			ID:   &accountId,
		},
		Permission: &permission,
	})
	grants = append(grants, &s3.Grant{
		Grantee: &s3.Grantee{
			Type: &s3_constants.GrantTypeCanonicalUser,
			ID:   &accountId,
		},
		Permission: &s3_constants.PermissionFullControl,
	})

	// group grantee (AuthenticateUsers)
	if accountId != s3account.AccountAnonymous.Id {
		grants = append(grants, &s3.Grant{
			Grantee: &s3.Grantee{
				Type: &s3_constants.GrantTypeGroup,
				URI:  &s3_constants.GranteeGroupAuthenticatedUsers,
			},
			Permission: &permission,
		})
		grants = append(grants, &s3.Grant{
			Grantee: &s3.Grantee{
				Type: &s3_constants.GrantTypeGroup,
				URI:  &s3_constants.GranteeGroupAuthenticatedUsers,
			},
			Permission: &s3_constants.PermissionFullControl,
		})
	}
	return
}

func SetAcpOwnerHeader(r *http.Request, acpOwnerId string) {
	r.Header.Set(s3_constants.ExtAmzOwnerKey, acpOwnerId)
}

func GetAcpOwner(entryExtended map[string][]byte, defaultOwner string) string {
	ownerIdBytes, ok := entryExtended[s3_constants.ExtAmzOwnerKey]
	if ok && len(ownerIdBytes) > 0 {
		return string(ownerIdBytes)
	}
	return defaultOwner
}

func SetAcpGrantsHeader(r *http.Request, grants []*s3.Grant) {
	if len(grants) > 0 {
		a, err := MarshalGrantsToJson(grants)
		if err == nil {
			r.Header.Set(s3_constants.ExtAmzAclKey, string(a))
		} else {
			glog.Warning("Marshal acp grants err", err)
		}
	}
}

// GetAcpGrants return grants parsed from entry
func GetAcpGrants(ownerId *string, entryExtended map[string][]byte) []*s3.Grant {
	acpBytes, ok := entryExtended[s3_constants.ExtAmzAclKey]
	if ok && len(acpBytes) > 0 {
		var grants []*s3.Grant
		err := json.Unmarshal(acpBytes, &grants)
		if err == nil {
			return grants
		}
		glog.Warning("grants Unmarshal error", err)
	}
	if ownerId == nil {
		return nil
	}
	return []*s3.Grant{
		{
			Grantee: &s3.Grantee{
				Type: &s3_constants.GrantTypeCanonicalUser,
				ID:   ownerId,
			},
			Permission: &s3_constants.PermissionFullControl,
		},
	}
}

// AssembleEntryWithAcp fill entry with owner and grants
func AssembleEntryWithAcp(filerEntry *filer_pb.Entry, ownerId string, grants []*s3.Grant) s3err.ErrorCode {
	if filerEntry.Extended == nil {
		filerEntry.Extended = make(map[string][]byte)
	}

	if len(ownerId) > 0 {
		filerEntry.Extended[s3_constants.ExtAmzOwnerKey] = []byte(ownerId)
	} else {
		delete(filerEntry.Extended, s3_constants.ExtAmzOwnerKey)
	}

	if grants != nil {
		grantsBytes, err := MarshalGrantsToJson(grants)
		if err != nil {
			glog.Warning("assemble acp to entry:", err)
			return s3err.ErrInvalidRequest
		}
		filerEntry.Extended[s3_constants.ExtAmzAclKey] = grantsBytes
	} else {
		delete(filerEntry.Extended, s3_constants.ExtAmzAclKey)
	}

	return s3err.ErrNone
}

// GrantEquals Compare whether two Grants are equal in meaning, not completely
// equal (compare Grantee.Type and the corresponding Value for equality, other
// fields of Grantee are ignored)
func GrantEquals(a, b *s3.Grant) bool {
	// grant
	if a == b {
		return true
	}

	if a == nil || b == nil {
		return false
	}

	// grant.Permission
	if a.Permission != b.Permission {
		if a.Permission == nil || b.Permission == nil {
			return false
		}

		if *a.Permission != *b.Permission {
			return false
		}
	}

	// grant.Grantee
	ag := a.Grantee
	bg := b.Grantee
	if ag != bg {
		if ag == nil || bg == nil {
			return false
		}
		// grantee.Type
		if ag.Type != bg.Type {
			if ag.Type == nil || bg.Type == nil {
				return false
			}
			if *ag.Type != *bg.Type {
				return false
			}
		}
		// value corresponding to granteeType
		if ag.Type != nil {
			switch *ag.Type {
			case s3_constants.GrantTypeGroup:
				if ag.URI != bg.URI {
					if ag.URI == nil || bg.URI == nil {
						return false
					}

					if *ag.URI != *bg.URI {
						return false
					}
				}
			case s3_constants.GrantTypeCanonicalUser:
				if ag.ID != bg.ID {
					if ag.ID == nil || bg.ID == nil {
						return false
					}

					if *ag.ID != *bg.ID {
						return false
					}
				}
			case s3_constants.GrantTypeAmazonCustomerByEmail:
				if ag.EmailAddress != bg.EmailAddress {
					if ag.EmailAddress == nil || bg.EmailAddress == nil {
						return false
					}

					if *ag.EmailAddress != *bg.EmailAddress {
						return false
					}
				}
			}
		}
	}
	return true
}

func MarshalGrantsToJson(grants []*s3.Grant) ([]byte, error) {
	if len(grants) == 0 {
		return []byte{}, nil
	}
	var GrantsToMap []map[string]any
	for _, grant := range grants {
		grantee := grant.Grantee
		switch *grantee.Type {
		case s3_constants.GrantTypeGroup:
			GrantsToMap = append(GrantsToMap, map[string]any{
				"Permission": grant.Permission,
				"Grantee": map[string]any{
					"Type": grantee.Type,
					"URI":  grantee.URI,
				},
			})
		case s3_constants.GrantTypeCanonicalUser:
			GrantsToMap = append(GrantsToMap, map[string]any{
				"Permission": grant.Permission,
				"Grantee": map[string]any{
					"Type": grantee.Type,
					"ID":   grantee.ID,
				},
			})
		case s3_constants.GrantTypeAmazonCustomerByEmail:
			GrantsToMap = append(GrantsToMap, map[string]any{
				"Permission": grant.Permission,
				"Grantee": map[string]any{
					"Type":         grantee.Type,
					"EmailAddress": grantee.EmailAddress,
				},
			})
		default:
			return nil, fmt.Errorf("grantee type[%s] is not valid", *grantee.Type)
		}
	}

	return json.Marshal(GrantsToMap)
}

func GrantWithFullControl(accountId string) *s3.Grant {
	return &s3.Grant{
		Permission: &s3_constants.PermissionFullControl,
		Grantee: &s3.Grantee{
			Type: &s3_constants.GrantTypeCanonicalUser,
			ID:   &accountId,
		},
	}
}

func CheckObjectAccessForReadObject(r *http.Request, w http.ResponseWriter, entry *filer.Entry, bucketOwnerId string) (statusCode int, ok bool) {
	if entry.IsDirectory() {
		return http.StatusOK, true
	}

	requestAccountId := GetAccountId(r)
	if len(requestAccountId) == 0 {
		glog.Warning("#checkObjectAccessForReadObject header[accountId] not exists!")
		return http.StatusForbidden, false
	}

	//owner access
	objectOwner := GetAcpOwner(entry.Extended, bucketOwnerId)
	if ValidateAccount(requestAccountId, objectOwner) {
		return http.StatusOK, true
	}

	//find in Grants
	acpGrants := GetAcpGrants(nil, entry.Extended)
	if acpGrants != nil {
		reqGrants := DetermineRequiredGrants(requestAccountId, s3_constants.PermissionRead)
		for _, requiredGrant := range reqGrants {
			for _, grant := range acpGrants {
				if GrantEquals(requiredGrant, grant) {
					return http.StatusOK, true
				}
			}
		}
	}

	glog.V(3).Infof("acl denied! request account id: %s", requestAccountId)
	return http.StatusForbidden, false
}