123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- package s3acl
- import (
- "encoding/json"
- "encoding/xml"
- "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
- "github.com/aws/aws-sdk-go/service/s3"
- "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"
- )
- // 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
- }
- }
- // ExtractAcl extracts the acl from the request body, or from the header if request body is empty
- func ExtractAcl(r *http.Request, accountManager *s3account.AccountManager, ownership, bucketOwnerId, ownerId, accountId string) (grants []*s3.Grant, errCode s3err.ErrorCode) {
- if r.Body != nil && r.Body != http.NoBody {
- defer util.CloseRequest(r)
- 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 != ownerId {
- glog.V(3).Infof("set acl denied! owner account is not consistent, request account id: %s, expect account id: %s", accountId, ownerId)
- return nil, s3err.ErrAccessDenied
- }
- return ValidateAndTransferGrants(accountManager, acp.Grants)
- } else {
- _, grants, errCode = ParseAndValidateAclHeadersOrElseDefault(r, accountManager, ownership, bucketOwnerId, accountId, true)
- return grants, errCode
- }
- }
- // ParseAndValidateAclHeadersOrElseDefault will callParseAndValidateAclHeaders to get Grants, if empty, it will return Grant that grant `accountId` with `FullControl` permission
- func ParseAndValidateAclHeadersOrElseDefault(r *http.Request, accountManager *s3account.AccountManager, ownership, bucketOwnerId, accountId string, putAcl bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) {
- ownerId, grants, errCode = ParseAndValidateAclHeaders(r, accountManager, ownership, bucketOwnerId, accountId, putAcl)
- if errCode != s3err.ErrNone {
- return
- }
- if len(grants) == 0 {
- //if no acl(both customAcl and cannedAcl) specified, grant accountId(object writer) with full control permission
- grants = append(grants, &s3.Grant{
- Grantee: &s3.Grantee{
- Type: &s3_constants.GrantTypeCanonicalUser,
- ID: &accountId,
- },
- Permission: &s3_constants.PermissionFullControl,
- })
- }
- return
- }
- // ParseAndValidateAclHeaders parse and validate acl from header
- func ParseAndValidateAclHeaders(r *http.Request, accountManager *s3account.AccountManager, ownership, bucketOwnerId, accountId string, putAcl bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) {
- ownerId, grants, errCode = ParseAclHeaders(r, ownership, bucketOwnerId, accountId, putAcl)
- if errCode != s3err.ErrNone {
- return
- }
- if len(grants) > 0 {
- grants, errCode = ValidateAndTransferGrants(accountManager, grants)
- }
- return
- }
- // ParseAclHeaders parse acl headers
- // When `putAcl` is true, only `CannedAcl` is parsed, such as `PutBucketAcl` or `PutObjectAcl`
- // is requested, `CustomAcl` is parsed from the request body not from headers, and only if the
- // request body is empty, `CannedAcl` is parsed from the header, and will not parse `CustomAcl` from the header
- //
- // Since `CustomAcl` has higher priority, it will be parsed first; if `CustomAcl` does not exist, `CannedAcl` will be parsed
- func ParseAclHeaders(r *http.Request, ownership, bucketOwnerId, accountId string, putAcl bool) (ownerId string, grants []*s3.Grant, errCode s3err.ErrorCode) {
- if !putAcl {
- errCode = ParseCustomAclHeaders(r, &grants)
- if errCode != s3err.ErrNone {
- return "", nil, errCode
- }
- }
- if len(grants) > 0 {
- return accountId, grants, s3err.ErrNone
- }
- cannedAcl := r.Header.Get(s3_constants.AmzCannedAcl)
- if len(cannedAcl) == 0 {
- return accountId, grants, s3err.ErrNone
- }
- //if canned acl specified, parse cannedAcl (lower priority to custom acl)
- ownerId, grants, errCode = ParseCannedAclHeader(ownership, bucketOwnerId, accountId, cannedAcl, putAcl)
- if errCode != s3err.ErrNone {
- return "", nil, errCode
- }
- return ownerId, grants, errCode
- }
- func ParseCustomAclHeaders(r *http.Request, grants *[]*s3.Grant) s3err.ErrorCode {
- customAclHeaders := []string{s3_constants.AmzAclFullControl, s3_constants.AmzAclRead, s3_constants.AmzAclReadAcp, s3_constants.AmzAclWrite, s3_constants.AmzAclWriteAcp}
- var errCode s3err.ErrorCode
- 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)
- }
- if errCode != s3err.ErrNone {
- return errCode
- }
- }
- return 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 kv[0] {
- case "id":
- var accountId string
- _ = json.Unmarshal([]byte(kv[1]), &accountId)
- *grants = append(*grants, &s3.Grant{
- Grantee: &s3.Grantee{
- Type: &s3_constants.GrantTypeCanonicalUser,
- ID: &accountId,
- },
- Permission: &permission,
- })
- case "emailAddress":
- var emailAddress string
- _ = json.Unmarshal([]byte(kv[1]), &emailAddress)
- *grants = append(*grants, &s3.Grant{
- Grantee: &s3.Grantee{
- Type: &s3_constants.GrantTypeAmazonCustomerByEmail,
- EmailAddress: &emailAddress,
- },
- Permission: &permission,
- })
- case "uri":
- var groupName string
- _ = json.Unmarshal([]byte(kv[1]), &groupName)
- *grants = append(*grants, &s3.Grant{
- Grantee: &s3.Grantee{
- Type: &s3_constants.GrantTypeGroup,
- URI: &groupName,
- },
- Permission: &permission,
- })
- }
- }
- }
- return s3err.ErrNone
- }
- func ParseCannedAclHeader(bucketOwnership, bucketOwnerId, accountId, cannedAcl string, putAcl bool) (ownerId string, grants []*s3.Grant, err s3err.ErrorCode) {
- err = s3err.ErrNone
- ownerId = accountId
- //objectWrite automatically has full control on current object
- objectWriterFullControl := &s3.Grant{
- Grantee: &s3.Grantee{
- ID: &accountId,
- 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 bucketOwnerId != "" && bucketOwnerId != accountId {
- 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 !putAcl && bucketOwnership == 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 accountId != bucketOwnerId {
- grants = append(grants,
- &s3.Grant{
- Grantee: &s3.Grantee{
- Type: &s3_constants.GrantTypeCanonicalUser,
- ID: &bucketOwnerId,
- },
- Permission: &s3_constants.PermissionFullControl,
- })
- }
- }
- }
- case s3_constants.CannedAclAwsExecRead:
- err = s3err.ErrNotImplemented
- default:
- err = s3err.ErrInvalidRequest
- }
- return
- }
- // ValidateAndTransferGrants validate grant & 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
- }
- // DetermineReqGrants generates the grant set (Grants) according to accountId and reqPermission.
- func DetermineReqGrants(accountId, aclAction string) (grants []*s3.Grant) {
- // group grantee (AllUsers)
- grants = append(grants, &s3.Grant{
- Grantee: &s3.Grantee{
- Type: &s3_constants.GrantTypeGroup,
- URI: &s3_constants.GranteeGroupAllUsers,
- },
- Permission: &aclAction,
- })
- 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: &aclAction,
- })
- 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: &aclAction,
- })
- 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, acpGrants []*s3.Grant) {
- if len(acpGrants) > 0 {
- a, err := json.Marshal(acpGrants)
- 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(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
- }
- }
- return nil
- }
- // AssembleEntryWithAcp fill entry with owner and grants
- func AssembleEntryWithAcp(objectEntry *filer_pb.Entry, objectOwner string, grants []*s3.Grant) s3err.ErrorCode {
- if objectEntry.Extended == nil {
- objectEntry.Extended = make(map[string][]byte)
- }
- if len(objectOwner) > 0 {
- objectEntry.Extended[s3_constants.ExtAmzOwnerKey] = []byte(objectOwner)
- } else {
- delete(objectEntry.Extended, s3_constants.ExtAmzOwnerKey)
- }
- if len(grants) > 0 {
- grantsBytes, err := json.Marshal(grants)
- if err != nil {
- glog.Warning("assemble acp to entry:", err)
- return s3err.ErrInvalidRequest
- }
- objectEntry.Extended[s3_constants.ExtAmzAclKey] = grantsBytes
- } else {
- delete(objectEntry.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
- }
|