From f8b94cac0e91660ff4dc33bb458e973b4de6523b Mon Sep 17 00:00:00 2001 From: Konstantin Lebedev <9497591+kmlebedev@users.noreply.github.com> Date: Mon, 25 Sep 2023 20:34:12 +0500 Subject: [s3acl] Step1: move s3account.AccountManager into to iam.S3ApiConfiguration (#4859) * move s3account.AccountManager into to iam.S3ApiConfiguration and switch to Interface https://github.com/seaweedfs/seaweedfs/issues/4519 * fix: test bucket acl default and adjust the variable names * fix: s3 api config test --------- Co-authored-by: Konstantin Lebedev <9497591+kmlebedev@users.noreply.github.co> Co-authored-by: Chris Lu --- Makefile | 5 +- docker/compose/s3.json | 14 +- docker/compose/s3tests.conf | 4 +- weed/pb/iam.proto | 9 +- weed/pb/iam_pb/iam.pb.go | 168 +++++++-- weed/pb/iam_pb/iam_grpc.pb.go | 6 +- weed/s3api/auth_credentials.go | 137 ++++++- weed/s3api/auth_credentials_test.go | 37 +- weed/s3api/bucket_metadata.go | 15 +- weed/s3api/bucket_metadata_test.go | 42 +-- weed/s3api/s3_constants/s3_acp.go | 6 + weed/s3api/s3account/s3_account.go | 70 ---- weed/s3api/s3acl/acl_helper.go | 509 -------------------------- weed/s3api/s3acl/acl_helper_test.go | 706 ----------------------------------- weed/s3api/s3api_acl_helper.go | 513 ++++++++++++++++++++++++++ weed/s3api/s3api_acl_helper_test.go | 709 ++++++++++++++++++++++++++++++++++++ weed/s3api/s3api_acp.go | 5 +- weed/s3api/s3api_bucket_handlers.go | 11 +- weed/s3api/s3api_server.go | 3 - 19 files changed, 1567 insertions(+), 1402 deletions(-) create mode 100644 weed/s3api/s3_constants/s3_acp.go delete mode 100644 weed/s3api/s3account/s3_account.go delete mode 100644 weed/s3api/s3acl/acl_helper.go delete mode 100644 weed/s3api/s3acl/acl_helper_test.go create mode 100644 weed/s3api/s3api_acl_helper.go create mode 100644 weed/s3api/s3api_acl_helper_test.go diff --git a/Makefile b/Makefile index 315a62428..6ca6cae0d 100644 --- a/Makefile +++ b/Makefile @@ -10,5 +10,8 @@ install: full_install: cd weed; go install -tags "elastic gocdk sqlite ydb tikv" -tests: +server: install + weed -v 4 server -s3 -filer -volume.max=0 -master.volumeSizeLimitMB=1024 -volume.preStopSeconds=1 -s3.port=8000 -s3.allowEmptyFolder=false -s3.allowDeleteBucketNotEmpty=false -s3.config=./docker/compose/s3.json + +test: cd weed; go test -tags "elastic gocdk sqlite ydb tikv" -v ./... diff --git a/docker/compose/s3.json b/docker/compose/s3.json index 64dedb681..ce230863b 100644 --- a/docker/compose/s3.json +++ b/docker/compose/s3.json @@ -40,7 +40,10 @@ "List", "Tagging", "Write" - ] + ], + "account": { + "id": "testid" + } }, { "name": "s3_tests_alt", @@ -101,5 +104,12 @@ "Write" ] } - ] + ], + "accounts": [ + { + "id" : "testid", + "displayName": "M. Tester", + "emailAddress": "tester@ceph.com" + } + ] } \ No newline at end of file diff --git a/docker/compose/s3tests.conf b/docker/compose/s3tests.conf index 2bffe20d4..68d9ddeb7 100644 --- a/docker/compose/s3tests.conf +++ b/docker/compose/s3tests.conf @@ -18,10 +18,10 @@ bucket prefix = yournamehere-{random}- [s3 main] # main display_name set in vstart.sh -display_name = s3_tests +display_name = M. Tester # main user_idname set in vstart.sh -user_id = s3_tests +user_id = testid # main email set in vstart.sh email = tester@ceph.com diff --git a/weed/pb/iam.proto b/weed/pb/iam.proto index 1a6027292..99bb65ef2 100644 --- a/weed/pb/iam.proto +++ b/weed/pb/iam.proto @@ -16,13 +16,14 @@ service SeaweedIdentityAccessManagement { message S3ApiConfiguration { repeated Identity identities = 1; + repeated Account accounts = 2; } message Identity { string name = 1; repeated Credential credentials = 2; repeated string actions = 3; - string accountId = 4; + Account account = 4; } message Credential { @@ -32,6 +33,12 @@ message Credential { // bool is_disabled = 4; } +message Account { + string id = 1; + string display_name = 2; + string email_address = 3; +} + /* message Policy { repeated Statement statements = 1; diff --git a/weed/pb/iam_pb/iam.pb.go b/weed/pb/iam_pb/iam.pb.go index 3c5578833..074e255e6 100644 --- a/weed/pb/iam_pb/iam.pb.go +++ b/weed/pb/iam_pb/iam.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v4.23.3 +// protoc v4.23.2 // source: iam.proto package iam_pb @@ -26,6 +26,7 @@ type S3ApiConfiguration struct { unknownFields protoimpl.UnknownFields Identities []*Identity `protobuf:"bytes,1,rep,name=identities,proto3" json:"identities,omitempty"` + Accounts []*Account `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty"` } func (x *S3ApiConfiguration) Reset() { @@ -67,6 +68,13 @@ func (x *S3ApiConfiguration) GetIdentities() []*Identity { return nil } +func (x *S3ApiConfiguration) GetAccounts() []*Account { + if x != nil { + return x.Accounts + } + return nil +} + type Identity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -75,7 +83,7 @@ type Identity struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Credentials []*Credential `protobuf:"bytes,2,rep,name=credentials,proto3" json:"credentials,omitempty"` Actions []string `protobuf:"bytes,3,rep,name=actions,proto3" json:"actions,omitempty"` - AccountId string `protobuf:"bytes,4,opt,name=accountId,proto3" json:"accountId,omitempty"` + Account *Account `protobuf:"bytes,4,opt,name=account,proto3" json:"account,omitempty"` } func (x *Identity) Reset() { @@ -131,11 +139,11 @@ func (x *Identity) GetActions() []string { return nil } -func (x *Identity) GetAccountId() string { +func (x *Identity) GetAccount() *Account { if x != nil { - return x.AccountId + return x.Account } - return "" + return nil } type Credential struct { @@ -193,36 +201,109 @@ func (x *Credential) GetSecretKey() string { return "" } +type Account struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + EmailAddress string `protobuf:"bytes,3,opt,name=email_address,json=emailAddress,proto3" json:"email_address,omitempty"` +} + +func (x *Account) Reset() { + *x = Account{} + if protoimpl.UnsafeEnabled { + mi := &file_iam_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Account) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Account) ProtoMessage() {} + +func (x *Account) ProtoReflect() protoreflect.Message { + mi := &file_iam_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Account.ProtoReflect.Descriptor instead. +func (*Account) Descriptor() ([]byte, []int) { + return file_iam_proto_rawDescGZIP(), []int{3} +} + +func (x *Account) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Account) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *Account) GetEmailAddress() string { + if x != nil { + return x.EmailAddress + } + return "" +} + var File_iam_proto protoreflect.FileDescriptor var file_iam_proto_rawDesc = []byte{ 0x0a, 0x09, 0x69, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x69, 0x61, 0x6d, - 0x5f, 0x70, 0x62, 0x22, 0x46, 0x0a, 0x12, 0x53, 0x33, 0x41, 0x70, 0x69, 0x43, 0x6f, 0x6e, 0x66, + 0x5f, 0x70, 0x62, 0x22, 0x73, 0x0a, 0x12, 0x53, 0x33, 0x41, 0x70, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x69, 0x61, 0x6d, 0x5f, 0x70, 0x62, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, - 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x08, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x0b, - 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x69, 0x61, 0x6d, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x4a, 0x0a, 0x0a, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x32, 0x21, 0x0a, 0x1f, 0x53, 0x65, 0x61, 0x77, 0x65, 0x65, - 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x4b, 0x0a, 0x10, 0x73, 0x65, 0x61, - 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x08, 0x49, - 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x73, 0x65, - 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, - 0x69, 0x61, 0x6d, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x08, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x69, 0x61, 0x6d, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x08, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x08, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x0b, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x69, 0x61, 0x6d, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x69, 0x61, 0x6d, + 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x4a, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, + 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, + 0x22, 0x61, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, + 0x0a, 0x0d, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x32, 0x21, 0x0a, 0x1f, 0x53, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x4b, 0x0a, 0x10, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, + 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x08, 0x49, 0x61, 0x6d, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, + 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x69, 0x61, 0x6d, + 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -237,20 +318,23 @@ func file_iam_proto_rawDescGZIP() []byte { return file_iam_proto_rawDescData } -var file_iam_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_iam_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_iam_proto_goTypes = []interface{}{ (*S3ApiConfiguration)(nil), // 0: iam_pb.S3ApiConfiguration (*Identity)(nil), // 1: iam_pb.Identity (*Credential)(nil), // 2: iam_pb.Credential + (*Account)(nil), // 3: iam_pb.Account } var file_iam_proto_depIdxs = []int32{ 1, // 0: iam_pb.S3ApiConfiguration.identities:type_name -> iam_pb.Identity - 2, // 1: iam_pb.Identity.credentials:type_name -> iam_pb.Credential - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 1: iam_pb.S3ApiConfiguration.accounts:type_name -> iam_pb.Account + 2, // 2: iam_pb.Identity.credentials:type_name -> iam_pb.Credential + 3, // 3: iam_pb.Identity.account:type_name -> iam_pb.Account + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_iam_proto_init() } @@ -295,6 +379,18 @@ func file_iam_proto_init() { return nil } } + file_iam_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -302,7 +398,7 @@ func file_iam_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_iam_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/weed/pb/iam_pb/iam_grpc.pb.go b/weed/pb/iam_pb/iam_grpc.pb.go index d3aee785d..c4fe7becc 100644 --- a/weed/pb/iam_pb/iam_grpc.pb.go +++ b/weed/pb/iam_pb/iam_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v4.23.3 +// - protoc-gen-go-grpc v1.3.0 +// - protoc v4.23.2 // source: iam.proto package iam_pb @@ -15,6 +15,8 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const () + // SeaweedIdentityAccessManagementClient is the client API for SeaweedIdentityAccessManagement service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. diff --git a/weed/s3api/auth_credentials.go b/weed/s3api/auth_credentials.go index 38ff2b5ca..a2b1fd90f 100644 --- a/weed/s3api/auth_credentials.go +++ b/weed/s3api/auth_credentials.go @@ -7,8 +7,6 @@ import ( "strings" "sync" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" - "github.com/seaweedfs/seaweedfs/weed/filer" "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/pb" @@ -29,6 +27,8 @@ type IdentityAccessManagement struct { identities []*Identity accessKeyIdent map[string]*Identity + accounts map[string]*Account + emailAccount map[string]*Account hashes map[string]*sync.Pool hashCounters map[string]*int32 identityAnonymous *Identity @@ -39,20 +39,50 @@ type IdentityAccessManagement struct { type Identity struct { Name string - AccountId string + Account *Account Credentials []*Credential Actions []Action } -func (i *Identity) isAnonymous() bool { - return i.Name == s3account.AccountAnonymous.Name +// Account represents a system user, a system user can +// configure multiple IAM-Users, IAM-Users can configure +// permissions respectively, and each IAM-User can +// configure multiple security credentials +type Account struct { + //Name is also used to display the "DisplayName" as the owner of the bucket or object + DisplayName string + EmailAddress string + + //Id is used to identify an Account when granting cross-account access(ACLs) to buckets and objects + Id string } +// Predefined Accounts +var ( + // AccountAdmin is used as the default account for IAM-Credentials access without Account configured + AccountAdmin = Account{ + DisplayName: "admin", + EmailAddress: "admin@example.com", + Id: s3_constants.AccountAdminId, + } + + // AccountAnonymous is used to represent the account for anonymous access + AccountAnonymous = Account{ + DisplayName: "anonymous", + EmailAddress: "anonymous@example.com", + Id: s3_constants.AccountAnonymousId, + } +) + type Credential struct { AccessKey string SecretKey string } +func (i *Identity) isAnonymous() bool { + return i.Account.Id == s3_constants.AccountAnonymousId +} + func (action Action) isAdmin() bool { return strings.HasPrefix(string(action), s3_constants.ACTION_ADMIN) } @@ -65,14 +95,19 @@ func (action Action) overBucket(bucket string) bool { return strings.HasSuffix(string(action), ":"+bucket) || strings.HasSuffix(string(action), ":*") } +// "Permission": "FULL_CONTROL"|"WRITE"|"WRITE_ACP"|"READ"|"READ_ACP" func (action Action) getPermission() Permission { switch act := strings.Split(string(action), ":")[0]; act { case s3_constants.ACTION_ADMIN: return Permission("FULL_CONTROL") case s3_constants.ACTION_WRITE: return Permission("WRITE") + case s3_constants.ACTION_WRITE_ACP: + return Permission("WRITE_ACP") case s3_constants.ACTION_READ: return Permission("READ") + case s3_constants.ACTION_READ_ACP: + return Permission("READ_ACP") default: return Permission("") } @@ -138,26 +173,69 @@ func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3Api var identities []*Identity var identityAnonymous *Identity accessKeyIdent := make(map[string]*Identity) + accounts := make(map[string]*Account) + emailAccount := make(map[string]*Account) + foundAccountAdmin := false + foundAccountAnonymous := false + + for _, account := range config.Accounts { + switch account.Id { + case AccountAdmin.Id: + AccountAdmin = Account{ + Id: account.Id, + DisplayName: account.DisplayName, + EmailAddress: account.EmailAddress, + } + accounts[account.Id] = &AccountAdmin + foundAccountAdmin = true + case AccountAnonymous.Id: + AccountAnonymous = Account{ + Id: account.Id, + DisplayName: account.DisplayName, + EmailAddress: account.EmailAddress, + } + accounts[account.Id] = &AccountAnonymous + foundAccountAnonymous = true + default: + t := Account{ + Id: account.Id, + DisplayName: account.DisplayName, + EmailAddress: account.EmailAddress, + } + accounts[account.Id] = &t + } + if account.EmailAddress != "" { + emailAccount[account.EmailAddress] = accounts[account.Id] + } + } + if !foundAccountAdmin { + accounts[AccountAdmin.Id] = &AccountAdmin + emailAccount[AccountAdmin.EmailAddress] = &AccountAdmin + } + if !foundAccountAnonymous { + accounts[AccountAnonymous.Id] = &AccountAnonymous + emailAccount[AccountAnonymous.EmailAddress] = &AccountAnonymous + } for _, ident := range config.Identities { t := &Identity{ Name: ident.Name, - AccountId: s3account.AccountAdmin.Id, Credentials: nil, Actions: nil, } - - if ident.Name == s3account.AccountAnonymous.Name { - if ident.AccountId != "" && ident.AccountId != s3account.AccountAnonymous.Id { - glog.Warningf("anonymous identity is associated with a non-anonymous account ID, the association is invalid") - } - t.AccountId = s3account.AccountAnonymous.Id + switch { + case ident.Name == AccountAnonymous.Id: + t.Account = &AccountAnonymous identityAnonymous = t - } else { - if len(ident.AccountId) > 0 { - t.AccountId = ident.AccountId + case ident.Account == nil: + t.Account = &AccountAdmin + default: + if account, ok := accounts[ident.Account.Id]; ok { + t.Account = account + } else { + t.Account = &AccountAdmin + glog.Warningf("identity %s is associated with a non exist account ID, the association is invalid", ident.Name) } } - for _, action := range ident.Actions { t.Actions = append(t.Actions, Action(action)) } @@ -170,15 +248,19 @@ func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3Api } identities = append(identities, t) } + iam.m.Lock() // atomically switch iam.identities = identities iam.identityAnonymous = identityAnonymous + iam.accounts = accounts + iam.emailAccount = emailAccount iam.accessKeyIdent = accessKeyIdent if !iam.isAuthEnabled { // one-directional, no toggling iam.isAuthEnabled = len(identities) > 0 } iam.m.Unlock() + return nil } @@ -209,6 +291,24 @@ func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, foun return nil, false } +func (iam *IdentityAccessManagement) GetAccountNameById(canonicalId string) string { + iam.m.RLock() + defer iam.m.RUnlock() + if account, ok := iam.accounts[canonicalId]; ok { + return account.DisplayName + } + return "" +} + +func (iam *IdentityAccessManagement) GetAccountIdByEmail(email string) string { + iam.m.RLock() + defer iam.m.RUnlock() + if account, ok := iam.emailAccount[email]; ok { + return account.Id + } + return "" +} + func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if !iam.isEnabled() { @@ -287,9 +387,8 @@ func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) return identity, s3err.ErrAccessDenied } - if !identity.isAnonymous() { - r.Header.Set(s3_constants.AmzAccountId, identity.AccountId) - } + r.Header.Set(s3_constants.AmzAccountId, identity.Account.Id) + return identity, s3err.ErrNone } diff --git a/weed/s3api/auth_credentials_test.go b/weed/s3api/auth_credentials_test.go index 0bbcce706..f9f87fc54 100644 --- a/weed/s3api/auth_credentials_test.go +++ b/weed/s3api/auth_credentials_test.go @@ -2,14 +2,12 @@ package s3api import ( . "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" "github.com/stretchr/testify/assert" "reflect" "testing" - jsonpb "google.golang.org/protobuf/encoding/protojson" - "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" + jsonpb "google.golang.org/protobuf/encoding/protojson" ) func TestIdentityListFileFormat(t *testing.T) { @@ -146,11 +144,22 @@ func TestCanDo(t *testing.T) { } type LoadS3ApiConfigurationTestCase struct { + pbAccount *iam_pb.Account pbIdent *iam_pb.Identity expectIdent *Identity } func TestLoadS3ApiConfiguration(t *testing.T) { + specifiedAccount := Account{ + Id: "specifiedAccountID", + DisplayName: "specifiedAccountName", + EmailAddress: "specifiedAccounEmail@example.com", + } + pbSpecifiedAccount := iam_pb.Account{ + Id: "specifiedAccountID", + DisplayName: "specifiedAccountName", + EmailAddress: "specifiedAccounEmail@example.com", + } testCases := map[string]*LoadS3ApiConfigurationTestCase{ "notSpecifyAccountId": { pbIdent: &iam_pb.Identity{ @@ -167,8 +176,8 @@ func TestLoadS3ApiConfiguration(t *testing.T) { }, }, expectIdent: &Identity{ - Name: "notSpecifyAccountId", - AccountId: s3account.AccountAdmin.Id, + Name: "notSpecifyAccountId", + Account: &AccountAdmin, Actions: []Action{ "Read", "Write", @@ -182,17 +191,18 @@ func TestLoadS3ApiConfiguration(t *testing.T) { }, }, "specifiedAccountID": { + pbAccount: &pbSpecifiedAccount, pbIdent: &iam_pb.Identity{ - Name: "specifiedAccountID", - AccountId: "specifiedAccountID", + Name: "specifiedAccountID", + Account: &pbSpecifiedAccount, Actions: []string{ "Read", "Write", }, }, expectIdent: &Identity{ - Name: "specifiedAccountID", - AccountId: "specifiedAccountID", + Name: "specifiedAccountID", + Account: &specifiedAccount, Actions: []Action{ "Read", "Write", @@ -208,8 +218,8 @@ func TestLoadS3ApiConfiguration(t *testing.T) { }, }, expectIdent: &Identity{ - Name: "anonymous", - AccountId: "anonymous", + Name: "anonymous", + Account: &AccountAnonymous, Actions: []Action{ "Read", "Write", @@ -223,6 +233,9 @@ func TestLoadS3ApiConfiguration(t *testing.T) { } for _, v := range testCases { config.Identities = append(config.Identities, v.pbIdent) + if v.pbAccount != nil { + config.Accounts = append(config.Accounts, v.pbAccount) + } } iam := IdentityAccessManagement{} @@ -234,7 +247,7 @@ func TestLoadS3ApiConfiguration(t *testing.T) { for _, ident := range iam.identities { tc := testCases[ident.Name] if !reflect.DeepEqual(ident, tc.expectIdent) { - t.Error("not expect") + t.Errorf("not expect for ident name %s", ident.Name) } } } diff --git a/weed/s3api/bucket_metadata.go b/weed/s3api/bucket_metadata.go index f4088e6b3..d1f487104 100644 --- a/weed/s3api/bucket_metadata.go +++ b/weed/s3api/bucket_metadata.go @@ -6,7 +6,6 @@ import ( "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" "math" @@ -19,7 +18,7 @@ var loadBucketMetadataFromFiler = func(r *BucketRegistry, bucketName string) (*B return nil, err } - return buildBucketMetadata(r.s3a.accountManager, entry), nil + return buildBucketMetadata(r.s3a.iam, entry), nil } type BucketMetaData struct { @@ -73,13 +72,13 @@ func (r *BucketRegistry) init() error { } func (r *BucketRegistry) LoadBucketMetadata(entry *filer_pb.Entry) { - bucketMetadata := buildBucketMetadata(r.s3a.accountManager, entry) + bucketMetadata := buildBucketMetadata(r.s3a.iam, entry) r.metadataCacheLock.Lock() defer r.metadataCacheLock.Unlock() r.metadataCache[entry.Name] = bucketMetadata } -func buildBucketMetadata(accountManager *s3account.AccountManager, entry *filer_pb.Entry) *BucketMetaData { +func buildBucketMetadata(accountManager AccountManager, entry *filer_pb.Entry) *BucketMetaData { entryJson, _ := json.Marshal(entry) glog.V(3).Infof("build bucket metadata,entry=%s", entryJson) bucketMetadata := &BucketMetaData{ @@ -90,8 +89,8 @@ func buildBucketMetadata(accountManager *s3account.AccountManager, entry *filer_ // Default owner: `AccountAdmin` Owner: &s3.Owner{ - ID: &s3account.AccountAdmin.Id, - DisplayName: &s3account.AccountAdmin.Name, + ID: &AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, }, } if entry.Extended != nil { @@ -112,8 +111,8 @@ func buildBucketMetadata(accountManager *s3account.AccountManager, entry *filer_ acpOwnerBytes, ok := entry.Extended[s3_constants.ExtAmzOwnerKey] if ok && len(acpOwnerBytes) > 0 { ownerAccountId := string(acpOwnerBytes) - ownerAccountName, exists := accountManager.IdNameMapping[ownerAccountId] - if !exists { + ownerAccountName := accountManager.GetAccountNameById(ownerAccountId) + if ownerAccountName == "" { glog.Warningf("owner[id=%s] is invalid, bucket: %s", ownerAccountId, bucketMetadata.Name) } else { bucketMetadata.Owner = &s3.Owner{ diff --git a/weed/s3api/bucket_metadata_test.go b/weed/s3api/bucket_metadata_test.go index f852a272a..16d20b8fe 100644 --- a/weed/s3api/bucket_metadata_test.go +++ b/weed/s3api/bucket_metadata_test.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/aws/aws-sdk-go/service/s3" "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" + "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "reflect" "sync" @@ -31,7 +31,7 @@ var ( Name: "entryWithValidAcp", Extended: map[string][]byte{ s3_constants.ExtOwnershipKey: []byte(s3_constants.OwnershipBucketOwnerEnforced), - s3_constants.ExtAmzOwnerKey: []byte(s3account.AccountAdmin.Name), + s3_constants.ExtAmzOwnerKey: []byte(AccountAdmin.DisplayName), s3_constants.ExtAmzAclKey: goodEntryAcl, }, } @@ -88,8 +88,8 @@ var tcs = []*BucketMetadataTestCase{ Name: badEntry.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -99,8 +99,8 @@ var tcs = []*BucketMetadataTestCase{ Name: goodEntry.Name, ObjectOwnership: s3_constants.OwnershipBucketOwnerEnforced, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: s3_constants.PublicRead, }, @@ -110,8 +110,8 @@ var tcs = []*BucketMetadataTestCase{ Name: ownershipEmptyStr.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -121,8 +121,8 @@ var tcs = []*BucketMetadataTestCase{ Name: ownershipValid.Name, ObjectOwnership: s3_constants.OwnershipBucketOwnerEnforced, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -132,8 +132,8 @@ var tcs = []*BucketMetadataTestCase{ Name: acpEmptyStr.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -143,8 +143,8 @@ var tcs = []*BucketMetadataTestCase{ Name: acpEmptyObject.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: nil, }, @@ -154,8 +154,8 @@ var tcs = []*BucketMetadataTestCase{ Name: acpOwnerNil.Name, ObjectOwnership: s3_constants.DefaultOwnershipForExists, Owner: &s3.Owner{ - DisplayName: &s3account.AccountAdmin.Name, - ID: &s3account.AccountAdmin.Id, + DisplayName: &AccountAdmin.DisplayName, + ID: &AccountAdmin.Id, }, Acl: make([]*s3.Grant, 0), }, @@ -163,14 +163,10 @@ var tcs = []*BucketMetadataTestCase{ } func TestBuildBucketMetadata(t *testing.T) { - accountManager := &s3account.AccountManager{ - IdNameMapping: map[string]string{ - s3account.AccountAdmin.Id: s3account.AccountAdmin.Name, - s3account.AccountAnonymous.Id: s3account.AccountAnonymous.Name, - }, - } + iam := &IdentityAccessManagement{} + _ = iam.loadS3ApiConfiguration(&iam_pb.S3ApiConfiguration{}) for _, tc := range tcs { - resultBucketMetadata := buildBucketMetadata(accountManager, tc.filerEntry) + resultBucketMetadata := buildBucketMetadata(iam, tc.filerEntry) if !reflect.DeepEqual(resultBucketMetadata, tc.expectBucketMetadata) { t.Fatalf("result is unexpect: \nresult: %v, \nexpect: %v", resultBucketMetadata, tc.expectBucketMetadata) } diff --git a/weed/s3api/s3_constants/s3_acp.go b/weed/s3api/s3_constants/s3_acp.go new file mode 100644 index 000000000..d24e07e24 --- /dev/null +++ b/weed/s3api/s3_constants/s3_acp.go @@ -0,0 +1,6 @@ +package s3_constants + +const ( + AccountAnonymousId = "anonymous" + AccountAdminId = "admin" +) diff --git a/weed/s3api/s3account/s3_account.go b/weed/s3api/s3account/s3_account.go deleted file mode 100644 index 9b1b01123..000000000 --- a/weed/s3api/s3account/s3_account.go +++ /dev/null @@ -1,70 +0,0 @@ -package s3account - -import ( - "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" - "sync" -) - -//Predefined Accounts -var ( - // AccountAdmin is used as the default account for IAM-Credentials access without Account configured - AccountAdmin = Account{ - Name: "admin", - EmailAddress: "admin@example.com", - Id: "admin", - } - - // AccountAnonymous is used to represent the account for anonymous access - AccountAnonymous = Account{ - Name: "anonymous", - EmailAddress: "anonymous@example.com", - Id: "anonymous", - } -) - -//Account represents a system user, a system user can -//configure multiple IAM-Users, IAM-Users can configure -//permissions respectively, and each IAM-User can -//configure multiple security credentials -type Account struct { - //Name is also used to display the "DisplayName" as the owner of the bucket or object - Name string - EmailAddress string - - //Id is used to identify an Account when granting cross-account access(ACLs) to buckets and objects - Id string -} - -type AccountManager struct { - sync.Mutex - filerClient filer_pb.FilerClient - - IdNameMapping map[string]string - EmailIdMapping map[string]string -} - -func NewAccountManager(filerClient filer_pb.FilerClient) *AccountManager { - am := &AccountManager{ - filerClient: filerClient, - IdNameMapping: make(map[string]string), - EmailIdMapping: make(map[string]string), - } - am.initialize() - return am -} - -func (am *AccountManager) GetAccountNameById(canonicalId string) string { - return am.IdNameMapping[canonicalId] -} - -func (am *AccountManager) GetAccountIdByEmail(email string) string { - return am.EmailIdMapping[email] -} - -func (am *AccountManager) initialize() { - // load predefined Accounts - for _, account := range []Account{AccountAdmin, AccountAnonymous} { - am.IdNameMapping[account.Id] = account.Name - am.EmailIdMapping[account.EmailAddress] = account.Id - } -} diff --git a/weed/s3api/s3acl/acl_helper.go b/weed/s3api/s3acl/acl_helper.go deleted file mode 100644 index bb956368e..000000000 --- a/weed/s3api/s3acl/acl_helper.go +++ /dev/null @@ -1,509 +0,0 @@ -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 -} diff --git a/weed/s3api/s3acl/acl_helper_test.go b/weed/s3api/s3acl/acl_helper_test.go deleted file mode 100644 index ce177595b..000000000 --- a/weed/s3api/s3acl/acl_helper_test.go +++ /dev/null @@ -1,706 +0,0 @@ -package s3acl - -import ( - "bytes" - "encoding/json" - "github.com/aws/aws-sdk-go/service/s3" - "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" - "io" - "net/http" - "testing" -) - -var ( - accountManager = &s3account.AccountManager{ - IdNameMapping: map[string]string{ - s3account.AccountAdmin.Id: s3account.AccountAdmin.Name, - s3account.AccountAnonymous.Id: s3account.AccountAnonymous.Name, - "accountA": "accountA", - "accountB": "accountB", - }, - EmailIdMapping: map[string]string{ - s3account.AccountAdmin.EmailAddress: s3account.AccountAdmin.Id, - s3account.AccountAnonymous.EmailAddress: s3account.AccountAnonymous.Id, - "accountA@example.com": "accountA", - "accountBexample.com": "accountB", - }, - } -) - -func TestGetAccountId(t *testing.T) { - req := &http.Request{ - Header: make(map[string][]string), - } - //case1 - //accountId: "admin" - req.Header.Set(s3_constants.AmzAccountId, s3account.AccountAdmin.Id) - if GetAccountId(req) != s3account.AccountAdmin.Id { - t.Fatal("expect accountId: admin") - } - - //case2 - //accountId: "anoymous" - req.Header.Set(s3_constants.AmzAccountId, s3account.AccountAnonymous.Id) - if GetAccountId(req) != s3account.AccountAnonymous.Id { - t.Fatal("expect accountId: anonymous") - } - - //case3 - //accountId is nil => "anonymous" - req.Header.Del(s3_constants.AmzAccountId) - if GetAccountId(req) != s3account.AccountAnonymous.Id { - t.Fatal("expect accountId: anonymous") - } -} - -func TestExtractAcl(t *testing.T) { - type Case struct { - id int - resultErrCode, expectErrCode s3err.ErrorCode - resultGrants, expectGrants []*s3.Grant - } - testCases := make([]*Case, 0) - accountAdminId := "admin" - - { - //case1 (good case) - //parse acp from request body - req := &http.Request{ - Header: make(map[string][]string), - } - req.Body = io.NopCloser(bytes.NewReader([]byte(` - - - admin - admin - - - - - admin - - FULL_CONTROL - - - - http://acs.amazonaws.com/groups/global/AllUsers - - FULL_CONTROL - - - - `))) - objectWriter := "accountA" - grants, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) - testCases = append(testCases, &Case{ - 1, - errCode, s3err.ErrNone, - grants, []*s3.Grant{ - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &accountAdminId, - }, - Permission: &s3_constants.PermissionFullControl, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - Permission: &s3_constants.PermissionFullControl, - }, - }, - }) - } - - { - //case2 (good case) - //parse acp from header (cannedAcl) - req := &http.Request{ - Header: make(map[string][]string), - } - req.Body = nil - req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclPrivate) - objectWriter := "accountA" - grants, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) - testCases = append(testCases, &Case{ - 2, - errCode, s3err.ErrNone, - grants, []*s3.Grant{ - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &objectWriter, - }, - Permission: &s3_constants.PermissionFullControl, - }, - }, - }) - } - - { - //case3 (bad case) - //parse acp from request body (content is invalid) - req := &http.Request{ - Header: make(map[string][]string), - } - req.Body = io.NopCloser(bytes.NewReader([]byte("zdfsaf"))) - req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclPrivate) - objectWriter := "accountA" - _, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) - testCases = append(testCases, &Case{ - id: 3, - resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, - }) - } - - //case4 (bad case) - //parse acp from header (cannedAcl is invalid) - req := &http.Request{ - Header: make(map[string][]string), - } - req.Body = nil - req.Header.Set(s3_constants.AmzCannedAcl, "dfaksjfk") - objectWriter := "accountA" - _, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, "", objectWriter) - testCases = append(testCases, &Case{ - id: 4, - resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, - }) - - { - //case5 (bad case) - //parse acp from request body: owner is inconsistent - req.Body = io.NopCloser(bytes.NewReader([]byte(` - - - admin - admin - - - - - admin - - FULL_CONTROL - - - - http://acs.amazonaws.com/groups/global/AllUsers - - FULL_CONTROL - - - - `))) - objectWriter = "accountA" - _, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, objectWriter, objectWriter) - testCases = append(testCases, &Case{ - id: 5, - resultErrCode: errCode, expectErrCode: s3err.ErrAccessDenied, - }) - } - - for _, tc := range testCases { - if tc.resultErrCode != tc.expectErrCode { - t.Fatalf("case[%d]: errorCode not expect", tc.id) - } - if !grantsEquals(tc.resultGrants, tc.expectGrants) { - t.Fatalf("case[%d]: grants not expect", tc.id) - } - } -} - -func TestParseAndValidateAclHeaders(t *testing.T) { - type Case struct { - id int - resultOwner, expectOwner string - resultErrCode, expectErrCode s3err.ErrorCode - resultGrants, expectGrants []*s3.Grant - } - testCases := make([]*Case, 0) - bucketOwner := "admin" - - { - //case1 (good case) - //parse custom acl - req := &http.Request{ - Header: make(map[string][]string), - } - objectWriter := "accountA" - req.Header.Set(s3_constants.AmzAclFullControl, `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="anonymous", emailAddress="admin@example.com"`) - ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) - testCases = append(testCases, &Case{ - 1, - ownerId, objectWriter, - errCode, s3err.ErrNone, - grants, []*s3.Grant{ - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - Permission: &s3_constants.PermissionFullControl, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &s3account.AccountAnonymous.Id, - }, - Permission: &s3_constants.PermissionFullControl, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &s3account.AccountAdmin.Id, - }, - Permission: &s3_constants.PermissionFullControl, - }, - }, - }) - } - { - //case2 (good case) - //parse canned acl (ownership=ObjectWriter) - req := &http.Request{ - Header: make(map[string][]string), - } - objectWriter := "accountA" - req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclBucketOwnerFullControl) - ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) - testCases = append(testCases, &Case{ - 2, - ownerId, objectWriter, - errCode, s3err.ErrNone, - grants, []*s3.Grant{ - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &objectWriter, - }, - Permission: &s3_constants.PermissionFullControl, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &bucketOwner, - }, - Permission: &s3_constants.PermissionFullControl, - }, - }, - }) - } - { - //case3 (good case) - //parse canned acl (ownership=OwnershipBucketOwnerPreferred) - req := &http.Request{ - Header: make(map[string][]string), - } - objectWriter := "accountA" - req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclBucketOwnerFullControl) - ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipBucketOwnerPreferred, bucketOwner, objectWriter, false) - testCases = append(testCases, &Case{ - 3, - ownerId, bucketOwner, - errCode, s3err.ErrNone, - grants, []*s3.Grant{ - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &bucketOwner, - }, - Permission: &s3_constants.PermissionFullControl, - }, - }, - }) - } - { - //case4 (bad case) - //parse custom acl (grantee id not exists) - req := &http.Request{ - Header: make(map[string][]string), - } - objectWriter := "accountA" - req.Header.Set(s3_constants.AmzAclFullControl, `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="notExistsAccount", emailAddress="admin@example.com"`) - _, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) - testCases = append(testCases, &Case{ - id: 4, - resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, - }) - } - - { - //case5 (bad case) - //parse custom acl (invalid format) - req := &http.Request{ - Header: make(map[string][]string), - } - objectWriter := "accountA" - req.Header.Set(s3_constants.AmzAclFullControl, `uri="http:sfasf"`) - _, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) - testCases = append(testCases, &Case{ - id: 5, - resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, - }) - } - - { - //case6 (bad case) - //parse canned acl (invalid value) - req := &http.Request{ - Header: make(map[string][]string), - } - objectWriter := "accountA" - req.Header.Set(s3_constants.AmzCannedAcl, `uri="http:sfasf"`) - _, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) - testCases = append(testCases, &Case{ - id: 5, - resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, - }) - } - - for _, tc := range testCases { - if tc.expectErrCode != tc.resultErrCode { - t.Errorf("case[%d]: errCode unexpect", tc.id) - } - if tc.resultOwner != tc.expectOwner { - t.Errorf("case[%d]: ownerId unexpect", tc.id) - } - if !grantsEquals(tc.resultGrants, tc.expectGrants) { - t.Fatalf("case[%d]: grants not expect", tc.id) - } - } -} - -func grantsEquals(a, b []*s3.Grant) bool { - if len(a) != len(b) { - return false - } - for i, grant := range a { - if !GrantEquals(grant, b[i]) { - return false - } - } - return true -} - -func TestDetermineReqGrants(t *testing.T) { - { - //case1: request account is anonymous - accountId := s3account.AccountAnonymous.Id - reqPermission := s3_constants.PermissionRead - - resultGrants := DetermineReqGrants(accountId, reqPermission) - expectGrants := []*s3.Grant{ - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - Permission: &reqPermission, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - Permission: &s3_constants.PermissionFullControl, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &accountId, - }, - Permission: &reqPermission, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &accountId, - }, - Permission: &s3_constants.PermissionFullControl, - }, - } - if !grantsEquals(resultGrants, expectGrants) { - t.Fatalf("grants not expect") - } - } - { - //case2: request account is not anonymous (Iam authed) - accountId := "accountX" - reqPermission := s3_constants.PermissionRead - - resultGrants := DetermineReqGrants(accountId, reqPermission) - expectGrants := []*s3.Grant{ - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - Permission: &reqPermission, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - Permission: &s3_constants.PermissionFullControl, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &accountId, - }, - Permission: &reqPermission, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeCanonicalUser, - ID: &accountId, - }, - Permission: &s3_constants.PermissionFullControl, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAuthenticatedUsers, - }, - Permission: &reqPermission, - }, - { - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAuthenticatedUsers, - }, - Permission: &s3_constants.PermissionFullControl, - }, - } - if !grantsEquals(resultGrants, expectGrants) { - t.Fatalf("grants not expect") - } - } -} - -func TestAssembleEntryWithAcp(t *testing.T) { - defaultOwner := "admin" - - //case1 - //assemble with non-empty grants - expectOwner := "accountS" - expectGrants := []*s3.Grant{ - { - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }, - } - entry := &filer_pb.Entry{} - AssembleEntryWithAcp(entry, expectOwner, expectGrants) - - resultOwner := GetAcpOwner(entry.Extended, defaultOwner) - if resultOwner != expectOwner { - t.Fatalf("owner not expect") - } - - resultGrants := GetAcpGrants(entry.Extended) - if !grantsEquals(resultGrants, expectGrants) { - t.Fatal("grants not expect") - } - - //case2 - //assemble with empty grants (override) - AssembleEntryWithAcp(entry, "", nil) - resultOwner = GetAcpOwner(entry.Extended, defaultOwner) - if resultOwner != defaultOwner { - t.Fatalf("owner not expect") - } - - resultGrants = GetAcpGrants(entry.Extended) - if len(resultGrants) != 0 { - t.Fatal("grants not expect") - } - -} - -func TestGrantEquals(t *testing.T) { - testCases := map[bool]bool{ - GrantEquals(nil, nil): true, - - GrantEquals(&s3.Grant{}, nil): false, - - GrantEquals(&s3.Grant{}, &s3.Grant{}): true, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - }, &s3.Grant{}): false, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - }): true, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{}, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{}, - }): true, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{}, - }): false, - - //type not present, compare other fields of grant is meaningless - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - ID: &s3account.AccountAdmin.Id, - EmailAddress: &s3account.AccountAdmin.EmailAddress, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - ID: &s3account.AccountAdmin.Id, - }, - }): true, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - }, - }): true, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }): true, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionWrite, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }): false, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, - }, - }): true, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, - }, - }): false, - - GrantEquals(&s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }, &s3.Grant{ - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }): true, - } - - for tc, expect := range testCases { - if tc != expect { - t.Fatal("TestGrantEquals not expect!") - } - } -} - -func TestSetAcpOwnerHeader(t *testing.T) { - ownerId := "accountZ" - req := &http.Request{ - Header: make(map[string][]string), - } - SetAcpOwnerHeader(req, ownerId) - - if req.Header.Get(s3_constants.ExtAmzOwnerKey) != ownerId { - t.Fatalf("owner unexpect") - } -} - -func TestSetAcpGrantsHeader(t *testing.T) { - req := &http.Request{ - Header: make(map[string][]string), - } - grants := []*s3.Grant{ - { - Permission: &s3_constants.PermissionRead, - Grantee: &s3.Grantee{ - Type: &s3_constants.GrantTypeGroup, - ID: &s3account.AccountAdmin.Id, - URI: &s3_constants.GranteeGroupAllUsers, - }, - }, - } - SetAcpGrantsHeader(req, grants) - - grantsJson, _ := json.Marshal(grants) - if req.Header.Get(s3_constants.ExtAmzAclKey) != string(grantsJson) { - t.Fatalf("owner unexpect") - } -} diff --git a/weed/s3api/s3api_acl_helper.go b/weed/s3api/s3api_acl_helper.go new file mode 100644 index 000000000..0332b6a39 --- /dev/null +++ b/weed/s3api/s3api_acl_helper.go @@ -0,0 +1,513 @@ +package s3api + +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/s3err" + "github.com/seaweedfs/seaweedfs/weed/util" + "net/http" + "strings" +) + +type AccountManager interface { + GetAccountNameById(canonicalId string) string + GetAccountIdByEmail(email string) string +} + +// 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 s3_constants.AccountAnonymousId + } 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 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 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 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 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 + } + name := accountManager.GetAccountNameById(*grantee.ID) + if len(name) == 0 { + 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 := accountManager.GetAccountIdByEmail(*grantee.EmailAddress) + if len(accountId) == 0 { + 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 != s3_constants.AccountAnonymousId { + 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 +} diff --git a/weed/s3api/s3api_acl_helper_test.go b/weed/s3api/s3api_acl_helper_test.go new file mode 100644 index 000000000..81ce25575 --- /dev/null +++ b/weed/s3api/s3api_acl_helper_test.go @@ -0,0 +1,709 @@ +package s3api + +import ( + "bytes" + "encoding/json" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" + "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" + "io" + "net/http" + "testing" +) + +var accountManager *IdentityAccessManagement + +func init() { + accountManager = &IdentityAccessManagement{} + _ = accountManager.loadS3ApiConfiguration(&iam_pb.S3ApiConfiguration{ + Accounts: []*iam_pb.Account{ + { + Id: "accountA", + DisplayName: "accountAName", + EmailAddress: "accountA@example.com", + }, + { + Id: "accountB", + DisplayName: "accountBName", + EmailAddress: "accountB@example.com", + }, + }, + }) +} + +func TestGetAccountId(t *testing.T) { + req := &http.Request{ + Header: make(map[string][]string), + } + //case1 + //accountId: "admin" + req.Header.Set(s3_constants.AmzAccountId, s3_constants.AccountAdminId) + if GetAccountId(req) != s3_constants.AccountAdminId { + t.Fatal("expect accountId: admin") + } + + //case2 + //accountId: "anoymous" + req.Header.Set(s3_constants.AmzAccountId, s3_constants.AccountAnonymousId) + if GetAccountId(req) != s3_constants.AccountAnonymousId { + t.Fatal("expect accountId: anonymous") + } + + //case3 + //accountId is nil => "anonymous" + req.Header.Del(s3_constants.AmzAccountId) + if GetAccountId(req) != s3_constants.AccountAnonymousId { + t.Fatal("expect accountId: anonymous") + } +} + +func TestExtractAcl(t *testing.T) { + type Case struct { + id int + resultErrCode, expectErrCode s3err.ErrorCode + resultGrants, expectGrants []*s3.Grant + } + testCases := make([]*Case, 0) + accountAdminId := "admin" + { + //case1 (good case) + //parse acp from request body + req := &http.Request{ + Header: make(map[string][]string), + } + req.Body = io.NopCloser(bytes.NewReader([]byte(` + + + admin + admin + + + + + admin + + FULL_CONTROL + + + + http://acs.amazonaws.com/groups/global/AllUsers + + FULL_CONTROL + + + + `))) + objectWriter := "accountA" + grants, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) + testCases = append(testCases, &Case{ + 1, + errCode, s3err.ErrNone, + grants, []*s3.Grant{ + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &accountAdminId, + }, + Permission: &s3_constants.PermissionFullControl, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + Permission: &s3_constants.PermissionFullControl, + }, + }, + }) + } + + { + //case2 (good case) + //parse acp from header (cannedAcl) + req := &http.Request{ + Header: make(map[string][]string), + } + req.Body = nil + req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclPrivate) + objectWriter := "accountA" + grants, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) + testCases = append(testCases, &Case{ + 2, + errCode, s3err.ErrNone, + grants, []*s3.Grant{ + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &objectWriter, + }, + Permission: &s3_constants.PermissionFullControl, + }, + }, + }) + } + + { + //case3 (bad case) + //parse acp from request body (content is invalid) + req := &http.Request{ + Header: make(map[string][]string), + } + req.Body = io.NopCloser(bytes.NewReader([]byte("zdfsaf"))) + req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclPrivate) + objectWriter := "accountA" + _, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, accountAdminId, objectWriter) + testCases = append(testCases, &Case{ + id: 3, + resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, + }) + } + + //case4 (bad case) + //parse acp from header (cannedAcl is invalid) + req := &http.Request{ + Header: make(map[string][]string), + } + req.Body = nil + req.Header.Set(s3_constants.AmzCannedAcl, "dfaksjfk") + objectWriter := "accountA" + _, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, "", objectWriter) + testCases = append(testCases, &Case{ + id: 4, + resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, + }) + + { + //case5 (bad case) + //parse acp from request body: owner is inconsistent + req.Body = io.NopCloser(bytes.NewReader([]byte(` + + + admin + admin + + + + + admin + + FULL_CONTROL + + + + http://acs.amazonaws.com/groups/global/AllUsers + + FULL_CONTROL + + + + `))) + objectWriter = "accountA" + _, errCode := ExtractAcl(req, accountManager, s3_constants.OwnershipObjectWriter, accountAdminId, objectWriter, objectWriter) + testCases = append(testCases, &Case{ + id: 5, + resultErrCode: errCode, expectErrCode: s3err.ErrAccessDenied, + }) + } + + for _, tc := range testCases { + if tc.resultErrCode != tc.expectErrCode { + t.Fatalf("case[%d]: errorCode not expect", tc.id) + } + if !grantsEquals(tc.resultGrants, tc.expectGrants) { + t.Fatalf("case[%d]: grants not expect", tc.id) + } + } +} + +func TestParseAndValidateAclHeaders(t *testing.T) { + type Case struct { + id int + resultOwner, expectOwner string + resultErrCode, expectErrCode s3err.ErrorCode + resultGrants, expectGrants []*s3.Grant + } + testCases := make([]*Case, 0) + bucketOwner := "admin" + + { + //case1 (good case) + //parse custom acl + req := &http.Request{ + Header: make(map[string][]string), + } + objectWriter := "accountA" + req.Header.Set(s3_constants.AmzAclFullControl, `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="anonymous", emailAddress="admin@example.com"`) + ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) + testCases = append(testCases, &Case{ + 1, + ownerId, objectWriter, + errCode, s3err.ErrNone, + grants, []*s3.Grant{ + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + Permission: &s3_constants.PermissionFullControl, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: aws.String(s3_constants.AccountAnonymousId), + }, + Permission: &s3_constants.PermissionFullControl, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: aws.String(s3_constants.AccountAdminId), + }, + Permission: &s3_constants.PermissionFullControl, + }, + }, + }) + } + { + //case2 (good case) + //parse canned acl (ownership=ObjectWriter) + req := &http.Request{ + Header: make(map[string][]string), + } + objectWriter := "accountA" + req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclBucketOwnerFullControl) + ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) + testCases = append(testCases, &Case{ + 2, + ownerId, objectWriter, + errCode, s3err.ErrNone, + grants, []*s3.Grant{ + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &objectWriter, + }, + Permission: &s3_constants.PermissionFullControl, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &bucketOwner, + }, + Permission: &s3_constants.PermissionFullControl, + }, + }, + }) + } + { + //case3 (good case) + //parse canned acl (ownership=OwnershipBucketOwnerPreferred) + req := &http.Request{ + Header: make(map[string][]string), + } + objectWriter := "accountA" + req.Header.Set(s3_constants.AmzCannedAcl, s3_constants.CannedAclBucketOwnerFullControl) + ownerId, grants, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipBucketOwnerPreferred, bucketOwner, objectWriter, false) + testCases = append(testCases, &Case{ + 3, + ownerId, bucketOwner, + errCode, s3err.ErrNone, + grants, []*s3.Grant{ + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &bucketOwner, + }, + Permission: &s3_constants.PermissionFullControl, + }, + }, + }) + } + { + //case4 (bad case) + //parse custom acl (grantee id not exists) + req := &http.Request{ + Header: make(map[string][]string), + } + objectWriter := "accountA" + req.Header.Set(s3_constants.AmzAclFullControl, `uri="http://acs.amazonaws.com/groups/global/AllUsers", id="notExistsAccount", emailAddress="admin@example.com"`) + _, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) + testCases = append(testCases, &Case{ + id: 4, + resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, + }) + } + + { + //case5 (bad case) + //parse custom acl (invalid format) + req := &http.Request{ + Header: make(map[string][]string), + } + objectWriter := "accountA" + req.Header.Set(s3_constants.AmzAclFullControl, `uri="http:sfasf"`) + _, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) + testCases = append(testCases, &Case{ + id: 5, + resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, + }) + } + + { + //case6 (bad case) + //parse canned acl (invalid value) + req := &http.Request{ + Header: make(map[string][]string), + } + objectWriter := "accountA" + req.Header.Set(s3_constants.AmzCannedAcl, `uri="http:sfasf"`) + _, _, errCode := ParseAndValidateAclHeaders(req, accountManager, s3_constants.OwnershipObjectWriter, bucketOwner, objectWriter, false) + testCases = append(testCases, &Case{ + id: 5, + resultErrCode: errCode, expectErrCode: s3err.ErrInvalidRequest, + }) + } + + for _, tc := range testCases { + if tc.expectErrCode != tc.resultErrCode { + t.Errorf("case[%d]: errCode unexpect", tc.id) + } + if tc.resultOwner != tc.expectOwner { + t.Errorf("case[%d]: ownerId unexpect", tc.id) + } + if !grantsEquals(tc.resultGrants, tc.expectGrants) { + t.Fatalf("case[%d]: grants not expect", tc.id) + } + } +} + +func grantsEquals(a, b []*s3.Grant) bool { + if len(a) != len(b) { + return false + } + for i, grant := range a { + if !GrantEquals(grant, b[i]) { + return false + } + } + return true +} + +func TestDetermineReqGrants(t *testing.T) { + { + //case1: request account is anonymous + accountId := s3_constants.AccountAnonymousId + reqPermission := s3_constants.PermissionRead + + resultGrants := DetermineReqGrants(accountId, reqPermission) + expectGrants := []*s3.Grant{ + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + Permission: &reqPermission, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + Permission: &s3_constants.PermissionFullControl, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &accountId, + }, + Permission: &reqPermission, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &accountId, + }, + Permission: &s3_constants.PermissionFullControl, + }, + } + if !grantsEquals(resultGrants, expectGrants) { + t.Fatalf("grants not expect") + } + } + { + //case2: request account is not anonymous (Iam authed) + accountId := "accountX" + reqPermission := s3_constants.PermissionRead + + resultGrants := DetermineReqGrants(accountId, reqPermission) + expectGrants := []*s3.Grant{ + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + Permission: &reqPermission, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + Permission: &s3_constants.PermissionFullControl, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &accountId, + }, + Permission: &reqPermission, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &accountId, + }, + Permission: &s3_constants.PermissionFullControl, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAuthenticatedUsers, + }, + Permission: &reqPermission, + }, + { + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAuthenticatedUsers, + }, + Permission: &s3_constants.PermissionFullControl, + }, + } + if !grantsEquals(resultGrants, expectGrants) { + t.Fatalf("grants not expect") + } + } +} + +func TestAssembleEntryWithAcp(t *testing.T) { + defaultOwner := "admin" + + //case1 + //assemble with non-empty grants + expectOwner := "accountS" + expectGrants := []*s3.Grant{ + { + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + ID: aws.String(s3_constants.AccountAdminId), + URI: &s3_constants.GranteeGroupAllUsers, + }, + }, + } + entry := &filer_pb.Entry{} + AssembleEntryWithAcp(entry, expectOwner, expectGrants) + + resultOwner := GetAcpOwner(entry.Extended, defaultOwner) + if resultOwner != expectOwner { + t.Fatalf("owner not expect") + } + + resultGrants := GetAcpGrants(entry.Extended) + if !grantsEquals(resultGrants, expectGrants) { + t.Fatal("grants not expect") + } + + //case2 + //assemble with empty grants (override) + AssembleEntryWithAcp(entry, "", nil) + resultOwner = GetAcpOwner(entry.Extended, defaultOwner) + if resultOwner != defaultOwner { + t.Fatalf("owner not expect") + } + + resultGrants = GetAcpGrants(entry.Extended) + if len(resultGrants) != 0 { + t.Fatal("grants not expect") + } + +} + +func TestGrantEquals(t *testing.T) { + testCases := map[bool]bool{ + GrantEquals(nil, nil): true, + + GrantEquals(&s3.Grant{}, nil): false, + + GrantEquals(&s3.Grant{}, &s3.Grant{}): true, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + }, &s3.Grant{}): false, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + }): true, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{}, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{}, + }): true, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{}, + }): false, + + //type not present, compare other fields of grant is meaningless + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + ID: aws.String(s3_constants.AccountAdminId), + //EmailAddress: &s3account.AccountAdmin.EmailAddress, + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + ID: aws.String(s3_constants.AccountAdminId), + }, + }): true, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + }, + }): true, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + }): true, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionWrite, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + }): false, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + ID: aws.String(s3_constants.AccountAdminId), + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + ID: aws.String(s3_constants.AccountAdminId), + }, + }): true, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + ID: aws.String(s3_constants.AccountAdminId), + URI: &s3_constants.GranteeGroupAllUsers, + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + ID: aws.String(s3_constants.AccountAdminId), + }, + }): false, + + GrantEquals(&s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + ID: aws.String(s3_constants.AccountAdminId), + URI: &s3_constants.GranteeGroupAllUsers, + }, + }, &s3.Grant{ + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + URI: &s3_constants.GranteeGroupAllUsers, + }, + }): true, + } + + for tc, expect := range testCases { + if tc != expect { + t.Fatal("TestGrantEquals not expect!") + } + } +} + +func TestSetAcpOwnerHeader(t *testing.T) { + ownerId := "accountZ" + req := &http.Request{ + Header: make(map[string][]string), + } + SetAcpOwnerHeader(req, ownerId) + + if req.Header.Get(s3_constants.ExtAmzOwnerKey) != ownerId { + t.Fatalf("owner unexpect") + } +} + +func TestSetAcpGrantsHeader(t *testing.T) { + req := &http.Request{ + Header: make(map[string][]string), + } + grants := []*s3.Grant{ + { + Permission: &s3_constants.PermissionRead, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeGroup, + ID: aws.String(s3_constants.AccountAdminId), + URI: &s3_constants.GranteeGroupAllUsers, + }, + }, + } + SetAcpGrantsHeader(req, grants) + + grantsJson, _ := json.Marshal(grants) + if req.Header.Get(s3_constants.ExtAmzAclKey) != string(grantsJson) { + t.Fatalf("owner unexpect") + } +} diff --git a/weed/s3api/s3api_acp.go b/weed/s3api/s3api_acp.go index 7a76c2a67..0a79990f5 100644 --- a/weed/s3api/s3api_acp.go +++ b/weed/s3api/s3api_acp.go @@ -2,7 +2,6 @@ package s3api import ( "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "net/http" ) @@ -10,7 +9,7 @@ import ( func getAccountId(r *http.Request) string { id := r.Header.Get(s3_constants.AmzAccountId) if len(id) == 0 { - return s3account.AccountAnonymous.Id + return AccountAnonymous.Id } else { return id } @@ -22,7 +21,7 @@ func (s3a *S3ApiServer) checkAccessByOwnership(r *http.Request, bucket string) s return errCode } accountId := getAccountId(r) - if accountId == s3account.AccountAdmin.Id || accountId == *metadata.Owner.ID { + if accountId == AccountAdmin.Id || accountId == *metadata.Owner.ID { return s3err.ErrNone } return s3err.ErrAccessDenied diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index d2e987a25..3bf5a598b 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -259,17 +259,18 @@ func (s3a *S3ApiServer) GetBucketAclHandler(w http.ResponseWriter, r *http.Reque return } - identityId := r.Header.Get(s3_constants.AmzIdentityId) + amzAccountId := r.Header.Get(s3_constants.AmzAccountId) + amzDisplayName := s3a.iam.GetAccountNameById(amzAccountId) response := AccessControlPolicy{ Owner: CanonicalUser{ - ID: identityId, - DisplayName: identityId, + ID: amzAccountId, + DisplayName: amzDisplayName, }, } response.AccessControlList.Grant = append(response.AccessControlList.Grant, Grant{ Grantee: Grantee{ - ID: identityId, - DisplayName: identityId, + ID: amzAccountId, + DisplayName: amzDisplayName, Type: "CanonicalUser", XMLXSI: "CanonicalUser", XMLNS: "http://www.w3.org/2001/XMLSchema-instance"}, diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index 77c488f4d..e38aae23d 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/seaweedfs/seaweedfs/weed/filer" "github.com/seaweedfs/seaweedfs/weed/pb/s3_pb" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" "net" "net/http" "strings" @@ -42,7 +41,6 @@ type S3ApiServer struct { randomClientId int32 filerGuard *security.Guard client *http.Client - accountManager *s3account.AccountManager bucketRegistry *BucketRegistry } @@ -63,7 +61,6 @@ func NewS3ApiServer(router *mux.Router, option *S3ApiServerOption) (s3ApiServer filerGuard: security.NewGuard([]string{}, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec), cb: NewCircuitBreaker(option), } - s3ApiServer.accountManager = s3account.NewAccountManager(s3ApiServer) s3ApiServer.bucketRegistry = NewBucketRegistry(s3ApiServer) if option.LocalFilerSocket == "" { s3ApiServer.client = &http.Client{Transport: &http.Transport{ -- cgit v1.2.3 From adabb63c1869259103145a8daf48af75ade10940 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:14:29 -0700 Subject: Bump github.com/aws/aws-sdk-go-v2/config from 1.18.39 to 1.18.42 (#4868) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.39 to 1.18.42. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.39...config/v1.18.42) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 13 ++++++------- go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index a500cbda3..9e5bba5cb 100644 --- a/go.mod +++ b/go.mod @@ -144,8 +144,8 @@ require ( github.com/arangodb/go-driver v1.6.0 github.com/armon/go-metrics v0.4.1 github.com/aws/aws-sdk-go-v2 v1.21.0 - github.com/aws/aws-sdk-go-v2/config v1.18.39 - github.com/aws/aws-sdk-go-v2/credentials v1.13.37 + github.com/aws/aws-sdk-go-v2/config v1.18.42 + github.com/aws/aws-sdk-go-v2/credentials v1.13.40 github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 github.com/fluent/fluent-logger-golang v1.9.0 github.com/golang-jwt/jwt/v5 v5.0.0 @@ -185,7 +185,7 @@ require ( github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect @@ -193,9 +193,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect github.com/aws/aws-sdk-go-v2/service/sns v1.21.1 // indirect github.com/aws/aws-sdk-go-v2/service/sqs v1.24.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.14.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 // indirect github.com/aws/smithy-go v1.14.2 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/boltdb/bolt v1.3.1 // indirect @@ -268,7 +268,6 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8 // indirect github.com/rclone/ftp v0.0.0-20230327202000-dadc1f64e87d // indirect - github.com/rdleal/intervalst v0.0.0-20221028215511-a098aa0d2cb8 // indirect github.com/rfjakob/eme v1.1.2 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect diff --git a/go.sum b/go.sum index 23bf14320..906fe6078 100644 --- a/go.sum +++ b/go.sum @@ -118,10 +118,10 @@ github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzR github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= -github.com/aws/aws-sdk-go-v2/config v1.18.39 h1:oPVyh6fuu/u4OiW4qcuQyEtk7U7uuNBmHmJSLg1AJsQ= -github.com/aws/aws-sdk-go-v2/config v1.18.39/go.mod h1:+NH/ZigdPckFpgB1TRcRuWCB/Kbbvkxc/iNAKTq5RhE= -github.com/aws/aws-sdk-go-v2/credentials v1.13.37 h1:BvEdm09+ZEh2XtN+PVHPcYwKY3wIeB6pw7vPRM4M9/U= -github.com/aws/aws-sdk-go-v2/credentials v1.13.37/go.mod h1:ACLrdkd4CLZyXOghZ8IYumQbcooAcp2jo/s2xsFH8IM= +github.com/aws/aws-sdk-go-v2/config v1.18.42 h1:28jHROB27xZwU0CB88giDSjz7M1Sba3olb5JBGwina8= +github.com/aws/aws-sdk-go-v2/config v1.18.42/go.mod h1:4AZM3nMMxwlG+eZlxvBKqwVbkDLlnN2a4UGTL6HjaZI= +github.com/aws/aws-sdk-go-v2/credentials v1.13.40 h1:s8yOkDh+5b1jUDhMBtngF6zKWLDs84chUk2Vk0c38Og= +github.com/aws/aws-sdk-go-v2/credentials v1.13.40/go.mod h1:VtEHVAAqDWASwdOqj/1huyT6uHbs5s8FUHfDQdky/Rs= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37/go.mod h1:Pdn4j43v49Kk6+82spO3Tu5gSeQXRsxo56ePPQAvFiA= @@ -130,8 +130,8 @@ github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPne github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31/go.mod h1:fTJDMe8LOFYtqiFFFeHA+SVMAwqLhoq0kcInYoLa9Js= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 h1:g+qlObJH4Kn4n21g69DjspU0hKTjWtq7naZ9OLCv0ew= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= @@ -148,12 +148,12 @@ github.com/aws/aws-sdk-go-v2/service/sns v1.21.1 h1:Q01Dph/7FaB41Z7EY+SoVPa/kMpL github.com/aws/aws-sdk-go-v2/service/sns v1.21.1/go.mod h1:laHbYFVzphXdCiT3gitfuCDA2Oukrt9p40jWK7OJLgc= github.com/aws/aws-sdk-go-v2/service/sqs v1.24.1 h1:KbGaxApdPOT2ZWqJiQY5ApnpNhUGbGTjYiKAidlFwp8= github.com/aws/aws-sdk-go-v2/service/sqs v1.24.1/go.mod h1:+phkm4aFvcM4jbsDRGoZ+mD8MMvksHF459Xpy5Z90f0= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 h1:pSB560BbVj9ZlJZF4WYj5zsytWHWKxg+NgyGV4B2L58= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvTZW2S44Lt9Mk2aYQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= +github.com/aws/aws-sdk-go-v2/service/sso v1.14.1 h1:YkNzx1RLS0F5qdf9v1Q8Cuv9NXCL2TkosOxhzlUPV64= +github.com/aws/aws-sdk-go-v2/service/sso v1.14.1/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 h1:8lKOidPkmSmfUtiTgtdXWgaKItCZ/g75/jEk6Ql6GsA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= +github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 h1:s4bioTgjSFRwOoyEFzAVCmFmoowBgjTR8gkrF/sQ4wk= +github.com/aws/aws-sdk-go-v2/service/sts v1.22.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.14.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -- cgit v1.2.3 From 51efba43fd1bb2ce1bf4c99b945708b657e72183 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:14:49 -0700 Subject: Bump gocloud.dev/pubsub/rabbitpubsub from 0.33.0 to 0.34.0 (#4870) Bumps [gocloud.dev/pubsub/rabbitpubsub](https://github.com/google/go-cloud) from 0.33.0 to 0.34.0. - [Release notes](https://github.com/google/go-cloud/releases) - [Commits](https://github.com/google/go-cloud/compare/v0.33.0...v0.34.0) --- updated-dependencies: - dependency-name: gocloud.dev/pubsub/rabbitpubsub dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9e5bba5cb..9ff4262b4 100644 --- a/go.mod +++ b/go.mod @@ -111,7 +111,7 @@ require ( go.opencensus.io v0.24.0 // indirect gocloud.dev v0.34.0 gocloud.dev/pubsub/natspubsub v0.33.0 - gocloud.dev/pubsub/rabbitpubsub v0.33.0 + gocloud.dev/pubsub/rabbitpubsub v0.34.0 golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 golang.org/x/image v0.11.0 diff --git a/go.sum b/go.sum index 906fe6078..117345601 100644 --- a/go.sum +++ b/go.sum @@ -965,8 +965,8 @@ gocloud.dev v0.34.0 h1:LzlQY+4l2cMtuNfwT2ht4+fiXwWf/NmPTnXUlLmGif4= gocloud.dev v0.34.0/go.mod h1:psKOachbnvY3DAOPbsFVmLIErwsbWPUG2H5i65D38vE= gocloud.dev/pubsub/natspubsub v0.33.0 h1:SwHh+PuDmZ3/xv1h/7VBRVlgiBI9Hw6vS7NcvJzW3Us= gocloud.dev/pubsub/natspubsub v0.33.0/go.mod h1:2hKJ2+CbUyuSv4pFkaU/Ay3buK5ACJMxqlfnVSXFZGs= -gocloud.dev/pubsub/rabbitpubsub v0.33.0 h1:zidscDuR/INRVV/MKepaVbx/XIhgGKSCosiqfhORDZE= -gocloud.dev/pubsub/rabbitpubsub v0.33.0/go.mod h1:hauJ0yE5eKuEBMT13npOAgERcQeAIyQvnzPwml0WFW8= +gocloud.dev/pubsub/rabbitpubsub v0.34.0 h1:JvgO79IoX59RHonnRJjp92Oo5ecjRX6O4t1jy38cO3I= +gocloud.dev/pubsub/rabbitpubsub v0.34.0/go.mod h1:IaipzdqxYtjd+SRK6yGXRzovNaokMAfqq7VJHozTZD8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -- cgit v1.2.3 From a345b6802df90a942f8c45982b7a9187ee2fbc20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:15:07 -0700 Subject: Bump github.com/go-redsync/redsync/v4 from 4.8.2 to 4.9.4 (#4871) Bumps [github.com/go-redsync/redsync/v4](https://github.com/go-redsync/redsync) from 4.8.2 to 4.9.4. - [Release notes](https://github.com/go-redsync/redsync/releases) - [Commits](https://github.com/go-redsync/redsync/compare/v4.8.2...v4.9.4) --- updated-dependencies: - dependency-name: github.com/go-redsync/redsync/v4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9ff4262b4..32ee236cf 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.1.1 // indirect github.com/go-redis/redis/v8 v8.11.5 - github.com/go-redsync/redsync/v4 v4.8.2 + github.com/go-redsync/redsync/v4 v4.9.4 github.com/go-sql-driver/mysql v1.7.1 github.com/go-zookeeper/zk v1.0.3 // indirect github.com/gocql/gocql v1.6.0 diff --git a/go.sum b/go.sum index 117345601..0204a15f8 100644 --- a/go.sum +++ b/go.sum @@ -303,8 +303,8 @@ github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRf github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-redsync/redsync/v4 v4.8.2 h1:/b+jidlwOnSOl605hmNsd+ohfr/qB1EpLiKnCYkiw28= -github.com/go-redsync/redsync/v4 v4.8.2/go.mod h1:83QPGYHk0Wt2LhFo3n9xCrUGPQrQvejVTVR08KX032g= +github.com/go-redsync/redsync/v4 v4.9.4 h1:vRmYusI+qF95XSpApHAdeu+RjyDvxBXbMthbc/x148c= +github.com/go-redsync/redsync/v4 v4.9.4/go.mod h1:RqBDXUw0q+u9FJTeD2gMzGtHeSVV93DiqGl10B9Hn/4= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -- cgit v1.2.3 From a132d445568f4de6dba617b83b42d9b9cfb1cbd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:15:21 -0700 Subject: Bump google.golang.org/grpc from 1.58.0 to 1.58.2 (#4872) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.0 to 1.58.2. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.58.0...v1.58.2) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 32ee236cf..8a183d019 100644 --- a/go.mod +++ b/go.mod @@ -124,7 +124,7 @@ require ( google.golang.org/api v0.141.0 google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/grpc v1.58.0 + google.golang.org/grpc v1.58.2 google.golang.org/protobuf v1.31.0 gopkg.in/inf.v0 v0.9.1 // indirect modernc.org/b v1.0.0 // indirect diff --git a/go.sum b/go.sum index 0204a15f8..fc21204fb 100644 --- a/go.sum +++ b/go.sum @@ -1404,8 +1404,8 @@ google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= -google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc/examples v0.0.0-20201112215255-90f1b3ee835b h1:NuxyvVZoDfHZwYW9LD4GJiF5/nhiSyP4/InTrvw9Ibk= google.golang.org/grpc/examples v0.0.0-20201112215255-90f1b3ee835b/go.mod h1:IBqQ7wSUJ2Ep09a8rMWFsg4fmI2r38zwsq8a0GgxXpM= google.golang.org/grpc/security/advancedtls v0.0.0-20220622233350-5cdb09fa29c1 h1:0emxaJWaG6CfrA9Nbe4aHWbFz5AXw2QPEJP0/f42LCE= -- cgit v1.2.3