mirror of
https://github.com/versity/versitygw.git
synced 2026-01-29 22:42:02 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7efee6ceb5 | ||
|
|
9fd22ca8e7 | ||
|
|
0011ccd80e | ||
|
|
4d02ac21c5 | ||
|
|
5dca7cfa85 | ||
|
|
754c221c4d | ||
|
|
9fbb63f15d | ||
|
|
0ea5db228d | ||
|
|
031d5d1d1f | ||
|
|
7ff89af6b5 | ||
|
|
bcd667c4d4 | ||
|
|
bda5738a67 | ||
|
|
af641e5368 | ||
|
|
d469a72213 |
16
.github/workflows/system.yml
vendored
16
.github/workflows/system.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
curl https://dl.min.io/client/mc/release/linux-amd64/mc --create-dirs -o /usr/local/bin/mc
|
||||
chmod 755 /usr/local/bin/mc
|
||||
|
||||
- name: Build and run
|
||||
- name: Build and run, posix backend
|
||||
run: |
|
||||
make testbin
|
||||
export AWS_ACCESS_KEY_ID=ABCDEFGHIJKLMNOPQRST
|
||||
@@ -54,6 +54,20 @@ jobs:
|
||||
mkdir /tmp/cover
|
||||
VERSITYGW_TEST_ENV=./tests/.env.default GOCOVERDIR=/tmp/cover ./tests/run_all.sh
|
||||
|
||||
#- name: Build and run, s3 backend
|
||||
# run: |
|
||||
# make testbin
|
||||
# export AWS_ACCESS_KEY_ID=ABCDEFGHIJKLMNOPQRST
|
||||
# export AWS_SECRET_ACCESS_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
|
||||
# export AWS_REGION=us-east-1
|
||||
# aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile versity_s3
|
||||
# aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile versity_s3
|
||||
# aws configure set aws_region $AWS_REGION --profile versity_s3
|
||||
# export AWS_ACCESS_KEY_ID_TWO=ABCDEFGHIJKLMNOPQRST
|
||||
# export AWS_SECRET_ACCESS_KEY_TWO=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
|
||||
# export WORKSPACE=$GITHUB_WORKSPACE
|
||||
# VERSITYGW_TEST_ENV=./tests/.env.s3.default GOCOVERDIR=/tmp/cover ./tests/run_all.sh
|
||||
|
||||
- name: Coverage report
|
||||
run: |
|
||||
go tool covdata percent -i=/tmp/cover
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
FROM --platform=linux/arm64 ubuntu:latest
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG SECRETS_FILE=tests/.secrets
|
||||
ARG CONFIG_FILE=tests/.env.docker
|
||||
|
||||
ENV TZ=Etc/UTC
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
@@ -12,6 +15,7 @@ RUN apt-get update && \
|
||||
tzdata \
|
||||
s3cmd \
|
||||
jq \
|
||||
bc \
|
||||
ca-certificates && \
|
||||
update-ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
@@ -57,11 +61,12 @@ USER tester
|
||||
COPY --chown=tester:tester . /home/tester
|
||||
|
||||
WORKDIR /home/tester
|
||||
RUN cp tests/.env.docker.default tests/.env.docker
|
||||
RUN cp ${CONFIG_FILE}.default $CONFIG_FILE
|
||||
#RUN cp tests/.env.docker.s3.default tests/.env.docker.s3
|
||||
RUN cp tests/s3cfg.local.default tests/s3cfg.local
|
||||
RUN make
|
||||
|
||||
RUN . tests/.secrets && \
|
||||
RUN . $SECRETS_FILE && \
|
||||
export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION AWS_PROFILE && \
|
||||
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile $AWS_PROFILE && \
|
||||
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile $AWS_PROFILE && \
|
||||
@@ -74,6 +79,6 @@ RUN openssl genpkey -algorithm RSA -out versitygw-docker.pem -pkeyopt rsa_keygen
|
||||
-subj "/C=US/ST=California/L=San Francisco/O=Versity/OU=Software/CN=versity.com"
|
||||
|
||||
ENV WORKSPACE=.
|
||||
ENV VERSITYGW_TEST_ENV=tests/.env.docker
|
||||
ENV VERSITYGW_TEST_ENV=$CONFIG_FILE
|
||||
|
||||
CMD ["tests/run_all.sh"]
|
||||
CMD ["tests/run_all.sh"]
|
||||
|
||||
18
README.md
18
README.md
@@ -8,17 +8,33 @@
|
||||
|
||||
[](https://github.com/versity/versitygw/blob/main/LICENSE)
|
||||
|
||||
**News:**<br>
|
||||
### Binary release builds
|
||||
Download [latest release](https://github.com/versity/versitygw/releases)
|
||||
| Linux/amd64 | Linux/arm64 | MacOS/amd64 | MacOS/arm64 | BSD/amd64 | BSD/arm64 |
|
||||
|:-----------:|:-----------:|:-----------:|:-----------:|:---------:|:---------:|
|
||||
| ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
|
||||
|
||||
### News
|
||||
* New performance analysis article [https://github.com/versity/versitygw/wiki/Performance](https://github.com/versity/versitygw/wiki/Performance)
|
||||
|
||||
### Mailing List
|
||||
Keep up to date with latest gateway announcements by signing up to the [versitygw mailing list](https://www.versity.com/products/versitygw#signup).
|
||||
|
||||
### Documentation
|
||||
See project [documentation](https://github.com/versity/versitygw/wiki) on the wiki.
|
||||
|
||||
### Need help?
|
||||
Ask questions in the [community discussions](https://github.com/versity/versitygw/discussions).
|
||||
<br>
|
||||
Contact [Versity Sales](https://www.versity.com/contact/) to discuss enterprise support.
|
||||
|
||||
### Use Cases
|
||||
* Share filesystem directory via S3 protocol
|
||||
* Proxy S3 requests to S3 storage
|
||||
* Simple to deploy S3 server with a single command
|
||||
* Protocol compatibility in `posix` allows common access to files via posix or S3
|
||||
|
||||
### Overview
|
||||
Versity Gateway, a simple to use tool for seamless inline translation between AWS S3 object commands and storage systems. The Versity Gateway bridges the gap between S3-reliant applications and other storage systems, enabling enhanced compatibility and integration while offering exceptional scalability.
|
||||
|
||||
The server translates incoming S3 API requests and transforms them into equivalent operations to the backend service. By leveraging this gateway server, applications can interact with the S3-compatible API on top of already existing storage systems. This project enables leveraging existing infrastructure investments while seamlessly integrating with S3-compatible systems, offering increased flexibility and compatibility in managing data storage.
|
||||
|
||||
90
auth/acl.go
90
auth/acl.go
@@ -15,12 +15,14 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/versity/versitygw/backend"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
)
|
||||
|
||||
@@ -203,13 +205,14 @@ func splitUnique(s, divider string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func VerifyACL(acl ACL, access string, permission types.Permission, isRoot bool) error {
|
||||
if isRoot {
|
||||
return nil
|
||||
}
|
||||
func verifyACL(acl ACL, access string, permission types.Permission) error {
|
||||
// Default disabled ACL case
|
||||
if acl.ACL == "" && len(acl.Grantees) == 0 {
|
||||
if acl.Owner == access {
|
||||
return nil
|
||||
}
|
||||
|
||||
if acl.Owner == access {
|
||||
return nil
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
if acl.ACL != "" {
|
||||
@@ -273,3 +276,78 @@ func IsAdminOrOwner(acct Account, isRoot bool, acl ACL) error {
|
||||
// Return access denied in all other cases
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
type AccessOptions struct {
|
||||
Acl ACL
|
||||
AclPermission types.Permission
|
||||
IsRoot bool
|
||||
Acc Account
|
||||
Bucket string
|
||||
Object string
|
||||
Action Action
|
||||
}
|
||||
|
||||
func VerifyAccess(ctx context.Context, be backend.Backend, opts AccessOptions) error {
|
||||
if opts.IsRoot {
|
||||
return nil
|
||||
}
|
||||
if opts.Acc.Role == RoleAdmin {
|
||||
return nil
|
||||
}
|
||||
if opts.Acc.Access == opts.Acl.Owner {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := verifyACL(opts.Acl, opts.Acc.Access, opts.AclPermission); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := verifyBucketPolicy(ctx, be, opts.Acc.Access, opts.Bucket, opts.Object, opts.Action); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyObjectCopyAccess(ctx context.Context, be backend.Backend, copySource string, opts AccessOptions) error {
|
||||
if opts.IsRoot {
|
||||
return nil
|
||||
}
|
||||
if opts.Acc.Role == RoleAdmin {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify destination bucket access
|
||||
if err := VerifyAccess(ctx, be, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
// Verify source bucket access
|
||||
srcBucket, srcObject, found := strings.Cut(copySource, "/")
|
||||
if !found {
|
||||
return s3err.GetAPIError(s3err.ErrInvalidCopySource)
|
||||
}
|
||||
|
||||
// Get source bucket ACL
|
||||
srcBucketACLBytes, err := be.GetBucketAcl(ctx, &s3.GetBucketAclInput{Bucket: &srcBucket})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var srcBucketAcl ACL
|
||||
if err := json.Unmarshal(srcBucketACLBytes, &srcBucketAcl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifyAccess(ctx, be, AccessOptions{
|
||||
Acl: srcBucketAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: opts.IsRoot,
|
||||
Acc: opts.Acc,
|
||||
Bucket: srcBucket,
|
||||
Object: srcObject,
|
||||
Action: GetObjectAction,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
145
auth/bucket_policy.go
Normal file
145
auth/bucket_policy.go
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright 2023 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/versity/versitygw/backend"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
)
|
||||
|
||||
type BucketPolicy struct {
|
||||
Statement []BucketPolicyItem `json:"Statement"`
|
||||
}
|
||||
|
||||
func (bp *BucketPolicy) Validate(bucket string, iam IAMService) error {
|
||||
for _, statement := range bp.Statement {
|
||||
err := statement.Validate(bucket, iam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *BucketPolicy) isAllowed(principal string, action Action, resource string) bool {
|
||||
for _, statement := range bp.Statement {
|
||||
if statement.isAllowed(principal, action, resource) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type BucketPolicyItem struct {
|
||||
Effect BucketPolicyAccessType `json:"Effect"`
|
||||
Principals Principals `json:"Principal"`
|
||||
Actions Actions `json:"Action"`
|
||||
Resources Resources `json:"Resource"`
|
||||
}
|
||||
|
||||
func (bpi *BucketPolicyItem) Validate(bucket string, iam IAMService) error {
|
||||
if err := bpi.Effect.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bpi.Principals.Validate(iam); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bpi.Resources.Validate(bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containsObjectAction := bpi.Resources.ContainsObjectPattern()
|
||||
containsBucketAction := bpi.Resources.ContainsBucketPattern()
|
||||
|
||||
for action := range bpi.Actions {
|
||||
isObjectAction := action.IsObjectAction()
|
||||
if isObjectAction && !containsObjectAction {
|
||||
return fmt.Errorf("unsupported object action '%v' on the specified resources", action)
|
||||
}
|
||||
if !isObjectAction && !containsBucketAction {
|
||||
return fmt.Errorf("unsupported bucket action '%v' on the specified resources", action)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bpi *BucketPolicyItem) isAllowed(principal string, action Action, resource string) bool {
|
||||
if bpi.Principals.Contains(principal) && bpi.Actions.FindMatch(action) && bpi.Resources.FindMatch(resource) {
|
||||
switch bpi.Effect {
|
||||
case BucketPolicyAccessTypeAllow:
|
||||
return true
|
||||
case BucketPolicyAccessTypeDeny:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getMalformedPolicyError(err error) error {
|
||||
return s3err.APIError{
|
||||
Code: "MalformedPolicy",
|
||||
Description: err.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func ValidatePolicyDocument(policyBin []byte, bucket string, iam IAMService) error {
|
||||
var policy BucketPolicy
|
||||
if err := json.Unmarshal(policyBin, &policy); err != nil {
|
||||
return getMalformedPolicyError(err)
|
||||
}
|
||||
|
||||
if err := policy.Validate(bucket, iam); err != nil {
|
||||
return getMalformedPolicyError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyBucketPolicy(ctx context.Context, be backend.Backend, access, bucket, object string, action Action) error {
|
||||
policyDoc, err := be.GetBucketPolicy(ctx, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If bucket policy is not set
|
||||
if len(policyDoc) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var bucketPolicy BucketPolicy
|
||||
if err := json.Unmarshal(policyDoc, &bucketPolicy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resource := bucket
|
||||
if object != "" {
|
||||
resource += "" + object
|
||||
}
|
||||
|
||||
if !bucketPolicy.isAllowed(access, action, resource) {
|
||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
218
auth/bucket_policy_actions.go
Normal file
218
auth/bucket_policy_actions.go
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright 2023 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Action string
|
||||
|
||||
const (
|
||||
GetBucketAclAction Action = "s3:GetBucketAcl"
|
||||
CreateBucketAction Action = "s3:CreateBucket"
|
||||
PutBucketAclAction Action = "s3:PutBucketAcl"
|
||||
DeleteBucketAction Action = "s3:DeleteBucket"
|
||||
PutBucketVersioningAction Action = "s3:PutBucketVersioning"
|
||||
GetBucketVersioningAction Action = "s3:GetBucketVersioning"
|
||||
PutBucketPolicyAction Action = "s3:PutBucketPolicy"
|
||||
GetBucketPolicyAction Action = "s3:GetBucketPolicy"
|
||||
DeleteBucketPolicyAction Action = "s3:DeleteBucketPolicy"
|
||||
AbortMultipartUploadAction Action = "s3:AbortMultipartUpload"
|
||||
ListMultipartUploadPartsAction Action = "s3:ListMultipartUploadParts"
|
||||
ListBucketMultipartUploadsAction Action = "s3:ListBucketMultipartUploads"
|
||||
PutObjectAction Action = "s3:PutObject"
|
||||
GetObjectAction Action = "s3:GetObject"
|
||||
DeleteObjectAction Action = "s3:DeleteObject"
|
||||
GetObjectAclAction Action = "s3:GetObjectAcl"
|
||||
GetObjectAttributesAction Action = "s3:GetObjectAttributes"
|
||||
PutObjectAclAction Action = "s3:PutObjectAcl"
|
||||
RestoreObjectAction Action = "s3:RestoreObject"
|
||||
GetBucketTaggingAction Action = "s3:GetBucketTagging"
|
||||
PutBucketTaggingAction Action = "s3:PutBucketTagging"
|
||||
GetObjectTaggingAction Action = "s3:GetObjectTagging"
|
||||
PutObjectTaggingAction Action = "s3:PutObjectTagging"
|
||||
DeleteObjectTaggingAction Action = "s3:DeleteObjectTagging"
|
||||
ListBucketVersionsAction Action = "s3:ListBucketVersions"
|
||||
ListBucketAction Action = "s3:ListBucket"
|
||||
AllActions Action = "s3:*"
|
||||
)
|
||||
|
||||
var supportedActionList = map[Action]struct{}{
|
||||
GetBucketAclAction: {},
|
||||
CreateBucketAction: {},
|
||||
PutBucketAclAction: {},
|
||||
DeleteBucketAction: {},
|
||||
PutBucketVersioningAction: {},
|
||||
GetBucketVersioningAction: {},
|
||||
PutBucketPolicyAction: {},
|
||||
GetBucketPolicyAction: {},
|
||||
DeleteBucketPolicyAction: {},
|
||||
AbortMultipartUploadAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
ListBucketMultipartUploadsAction: {},
|
||||
PutObjectAction: {},
|
||||
GetObjectAction: {},
|
||||
DeleteObjectAction: {},
|
||||
GetObjectAclAction: {},
|
||||
GetObjectAttributesAction: {},
|
||||
PutObjectAclAction: {},
|
||||
RestoreObjectAction: {},
|
||||
GetBucketTaggingAction: {},
|
||||
PutBucketTaggingAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
DeleteObjectTaggingAction: {},
|
||||
ListBucketVersionsAction: {},
|
||||
ListBucketAction: {},
|
||||
AllActions: {},
|
||||
}
|
||||
|
||||
var supportedObjectActionList = map[Action]struct{}{
|
||||
AbortMultipartUploadAction: {},
|
||||
ListMultipartUploadPartsAction: {},
|
||||
PutObjectAction: {},
|
||||
GetObjectAction: {},
|
||||
DeleteObjectAction: {},
|
||||
GetObjectAclAction: {},
|
||||
GetObjectAttributesAction: {},
|
||||
PutObjectAclAction: {},
|
||||
RestoreObjectAction: {},
|
||||
GetObjectTaggingAction: {},
|
||||
PutObjectTaggingAction: {},
|
||||
DeleteObjectTaggingAction: {},
|
||||
AllActions: {},
|
||||
}
|
||||
|
||||
// Validates Action: it should either wildcard match with supported actions list or be in it
|
||||
func (a Action) IsValid() error {
|
||||
if !strings.HasPrefix(string(a), "s3:") {
|
||||
return fmt.Errorf("invalid action: %v", a)
|
||||
}
|
||||
|
||||
if a == AllActions {
|
||||
return nil
|
||||
}
|
||||
|
||||
if a[len(a)-1] == '*' {
|
||||
pattern := strings.TrimSuffix(string(a), "*")
|
||||
for act := range supportedActionList {
|
||||
if strings.HasPrefix(string(act), pattern) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("invalid wildcard usage: %v prefix is not in the supported actions list", pattern)
|
||||
}
|
||||
|
||||
_, found := supportedActionList[a]
|
||||
if !found {
|
||||
return fmt.Errorf("unsupported action: %v", a)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks if the action is object action
|
||||
func (a Action) IsObjectAction() bool {
|
||||
if a[len(a)-1] == '*' {
|
||||
pattern := strings.TrimSuffix(string(a), "*")
|
||||
for act := range supportedObjectActionList {
|
||||
if strings.HasPrefix(string(act), pattern) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
_, found := supportedObjectActionList[a]
|
||||
return found
|
||||
}
|
||||
|
||||
func (a Action) WildCardMatch(act Action) bool {
|
||||
if strings.HasSuffix(string(a), "*") {
|
||||
pattern := strings.TrimSuffix(string(a), "*")
|
||||
return strings.HasPrefix(string(act), pattern)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Actions map[Action]struct{}
|
||||
|
||||
// Override UnmarshalJSON method to decode both []string and string properties
|
||||
func (a *Actions) UnmarshalJSON(data []byte) error {
|
||||
ss := []string{}
|
||||
var err error
|
||||
if err = json.Unmarshal(data, &ss); err == nil {
|
||||
if len(ss) == 0 {
|
||||
return fmt.Errorf("actions can't be empty")
|
||||
}
|
||||
*a = make(Actions)
|
||||
for _, s := range ss {
|
||||
err = a.Add(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err == nil {
|
||||
if s == "" {
|
||||
return fmt.Errorf("actions can't be empty")
|
||||
}
|
||||
*a = make(Actions)
|
||||
err = a.Add(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Validates and adds a new Action to Actions map
|
||||
func (a Actions) Add(str string) error {
|
||||
action := Action(str)
|
||||
err := action.IsValid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a[action] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a Actions) FindMatch(action Action) bool {
|
||||
_, ok := a[AllActions]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
// First O(1) check for non wildcard actions
|
||||
_, found := a[action]
|
||||
if found {
|
||||
return true
|
||||
}
|
||||
|
||||
for act := range a {
|
||||
if strings.HasSuffix(string(act), "*") && act.WildCardMatch(action) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
34
auth/bucket_policy_effect.go
Normal file
34
auth/bucket_policy_effect.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2023 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import "fmt"
|
||||
|
||||
type BucketPolicyAccessType string
|
||||
|
||||
const (
|
||||
BucketPolicyAccessTypeDeny BucketPolicyAccessType = "Deny"
|
||||
BucketPolicyAccessTypeAllow BucketPolicyAccessType = "Allow"
|
||||
)
|
||||
|
||||
// Checks policy statement Effect to be valid ("Deny", "Allow")
|
||||
func (bpat BucketPolicyAccessType) Validate() error {
|
||||
switch bpat {
|
||||
case BucketPolicyAccessTypeAllow, BucketPolicyAccessTypeDeny:
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("invalid effect: %v", bpat)
|
||||
}
|
||||
97
auth/bucket_policy_principals.go
Normal file
97
auth/bucket_policy_principals.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2023 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Principals map[string]struct{}
|
||||
|
||||
func (p Principals) Add(key string) {
|
||||
p[key] = struct{}{}
|
||||
}
|
||||
|
||||
// Override UnmarshalJSON method to decode both []string and string properties
|
||||
func (p *Principals) UnmarshalJSON(data []byte) error {
|
||||
ss := []string{}
|
||||
var err error
|
||||
if err = json.Unmarshal(data, &ss); err == nil {
|
||||
if len(ss) == 0 {
|
||||
return fmt.Errorf("principals can't be empty")
|
||||
}
|
||||
*p = make(Principals)
|
||||
for _, s := range ss {
|
||||
p.Add(s)
|
||||
}
|
||||
} else {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err == nil {
|
||||
if s == "" {
|
||||
return fmt.Errorf("principals can't be empty")
|
||||
}
|
||||
*p = make(Principals)
|
||||
p.Add(s)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Converts Principals map to a slice, by omitting "*"
|
||||
func (p Principals) ToSlice() []string {
|
||||
principals := []string{}
|
||||
for p := range p {
|
||||
if p == "*" {
|
||||
continue
|
||||
}
|
||||
principals = append(principals, p)
|
||||
}
|
||||
|
||||
return principals
|
||||
}
|
||||
|
||||
// Validates Principals by checking user account access keys existence
|
||||
func (p Principals) Validate(iam IAMService) error {
|
||||
_, containsWildCard := p["*"]
|
||||
if containsWildCard {
|
||||
if len(p) == 1 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("principals should either contain * or user access keys")
|
||||
}
|
||||
|
||||
accs, err := CheckIfAccountsExist(p.ToSlice(), iam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(accs) > 0 {
|
||||
return fmt.Errorf("user accounts don't exist: %v", accs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Principals) Contains(userAccess string) bool {
|
||||
// "*" means it matches for any user account
|
||||
_, ok := p["*"]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
_, found := p[userAccess]
|
||||
return found
|
||||
}
|
||||
142
auth/bucket_policy_resources.go
Normal file
142
auth/bucket_policy_resources.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2023 Versity Software
|
||||
// This file is licensed under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Resources map[string]struct{}
|
||||
|
||||
const ResourceArnPrefix = "arn:aws:s3:::"
|
||||
|
||||
// Override UnmarshalJSON method to decode both []string and string properties
|
||||
func (r *Resources) UnmarshalJSON(data []byte) error {
|
||||
ss := []string{}
|
||||
var err error
|
||||
if err = json.Unmarshal(data, &ss); err == nil {
|
||||
if len(ss) == 0 {
|
||||
return fmt.Errorf("resources can't be empty")
|
||||
}
|
||||
*r = make(Resources)
|
||||
for _, s := range ss {
|
||||
err = r.Add(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err == nil {
|
||||
if s == "" {
|
||||
return fmt.Errorf("resources can't be empty")
|
||||
}
|
||||
*r = make(Resources)
|
||||
err = r.Add(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Adds and validates a new resource to Resources map
|
||||
func (r Resources) Add(rc string) error {
|
||||
ok, pattern := isValidResource(rc)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid resource: %v", rc)
|
||||
}
|
||||
|
||||
_, found := r[pattern]
|
||||
if found {
|
||||
return fmt.Errorf("duplicate resource: %v", rc)
|
||||
}
|
||||
|
||||
r[pattern] = struct{}{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks if the resources contain object pattern
|
||||
func (r Resources) ContainsObjectPattern() bool {
|
||||
for resource := range r {
|
||||
if resource == "*" || strings.Contains(resource, "/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if the resources contain bucket pattern
|
||||
func (r Resources) ContainsBucketPattern() bool {
|
||||
for resource := range r {
|
||||
if resource == "*" || !strings.Contains(resource, "/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Bucket resources should start with bucket name: arn:aws:s3:::MyBucket/*
|
||||
func (r Resources) Validate(bucket string) error {
|
||||
for resource := range r {
|
||||
if !strings.HasPrefix(resource, bucket) {
|
||||
return fmt.Errorf("incorrect bucket name in %v", resource)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Resources) FindMatch(resource string) bool {
|
||||
for res := range r {
|
||||
if strings.HasSuffix(res, "*") {
|
||||
pattern := strings.TrimSuffix(res, "*")
|
||||
if strings.HasPrefix(resource, pattern) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if res == resource {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks the resource to have arn prefix and not starting with /
|
||||
func isValidResource(rc string) (isValid bool, pattern string) {
|
||||
if !strings.HasPrefix(rc, ResourceArnPrefix) {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
res := strings.TrimPrefix(rc, ResourceArnPrefix)
|
||||
if res == "" {
|
||||
return false, ""
|
||||
}
|
||||
// The resource can't start with / (bucket name comes first)
|
||||
if strings.HasPrefix(res, "/") {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
return true, res
|
||||
}
|
||||
@@ -466,16 +466,6 @@ func (az *Azure) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3
|
||||
return nil, azureErrToS3Err(err)
|
||||
}
|
||||
|
||||
dstContainerAcl, err := getAclFromMetadata(res.Metadata, aclKeyCapital)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = auth.VerifyACL(*dstContainerAcl, *input.ExpectedBucketOwner, types.PermissionWrite, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.Join([]string{*input.Bucket, *input.Key}, "/") == *input.CopySource && isMetaSame(res.Metadata, input.Metadata) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrInvalidCopyDest)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ type Backend interface {
|
||||
GetBucketVersioning(_ context.Context, bucket string) (*s3.GetBucketVersioningOutput, error)
|
||||
PutBucketPolicy(_ context.Context, bucket string, policy []byte) error
|
||||
GetBucketPolicy(_ context.Context, bucket string) ([]byte, error)
|
||||
DeleteBucketPolicy(_ context.Context, bucket string) error
|
||||
|
||||
// multipart operations
|
||||
CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error)
|
||||
CompleteMultipartUpload(context.Context, *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error)
|
||||
@@ -125,6 +127,9 @@ func (BackendUnsupported) PutBucketPolicy(_ context.Context, bucket string, poli
|
||||
func (BackendUnsupported) GetBucketPolicy(_ context.Context, bucket string) ([]byte, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
func (BackendUnsupported) DeleteBucketPolicy(_ context.Context, bucket string) error {
|
||||
return s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
}
|
||||
|
||||
func (BackendUnsupported) CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
|
||||
|
||||
@@ -61,6 +61,7 @@ const (
|
||||
emptyMD5 = "d41d8cd98f00b204e9800998ecf8427e"
|
||||
aclkey = "user.acl"
|
||||
etagkey = "user.etag"
|
||||
policykey = "user.policy"
|
||||
)
|
||||
|
||||
func New(rootdir string) (*Posix, error) {
|
||||
@@ -1428,7 +1429,6 @@ func (p *Posix) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3.
|
||||
}
|
||||
dstBucket := *input.Bucket
|
||||
dstObject := *input.Key
|
||||
owner := *input.ExpectedBucketOwner
|
||||
|
||||
_, err := os.Stat(srcBucket)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
@@ -1446,22 +1446,6 @@ func (p *Posix) CopyObject(ctx context.Context, input *s3.CopyObjectInput) (*s3.
|
||||
return nil, fmt.Errorf("stat bucket: %w", err)
|
||||
}
|
||||
|
||||
dstBucketACLBytes, err := xattr.Get(dstBucket, aclkey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get dst bucket acl tag: %w", err)
|
||||
}
|
||||
|
||||
var dstBucketACL auth.ACL
|
||||
err = json.Unmarshal(dstBucketACLBytes, &dstBucketACL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse dst bucket acl: %w", err)
|
||||
}
|
||||
|
||||
err = auth.VerifyACL(dstBucketACL, owner, types.PermissionWrite, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objPath := filepath.Join(srcBucket, srcObject)
|
||||
f, err := os.Open(objPath)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
@@ -1857,6 +1841,58 @@ func (p *Posix) DeleteObjectTagging(ctx context.Context, bucket, object string)
|
||||
return p.PutObjectTagging(ctx, bucket, object, nil)
|
||||
}
|
||||
|
||||
func (p *Posix) PutBucketPolicy(ctx context.Context, bucket string, policy []byte) error {
|
||||
_, err := os.Stat(bucket)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return s3err.GetAPIError(s3err.ErrNoSuchBucket)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat bucket: %w", err)
|
||||
}
|
||||
|
||||
if policy == nil {
|
||||
if err := xattr.Remove(bucket, policykey); err != nil {
|
||||
if isNoAttr(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("remove policy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := xattr.Set(bucket, policykey, policy); err != nil {
|
||||
return fmt.Errorf("set policy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Posix) GetBucketPolicy(ctx context.Context, bucket string) ([]byte, error) {
|
||||
_, err := os.Stat(bucket)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, s3err.GetAPIError(s3err.ErrNoSuchBucket)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stat bucket: %w", err)
|
||||
}
|
||||
|
||||
policy, err := xattr.Get(bucket, policykey)
|
||||
if isNoAttr(err) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get bucket policy: %w", err)
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (p *Posix) DeleteBucketPolicy(ctx context.Context, bucket string) error {
|
||||
return p.PutBucketPolicy(ctx, bucket, nil)
|
||||
}
|
||||
|
||||
func (p *Posix) ChangeBucketOwner(ctx context.Context, bucket, newOwner string) error {
|
||||
_, err := os.Stat(bucket)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
|
||||
@@ -139,7 +139,10 @@ func (s *ScoutFS) CompleteMultipartUpload(_ context.Context, input *s3.CompleteM
|
||||
partsize := int64(0)
|
||||
var totalsize int64
|
||||
for i, p := range parts {
|
||||
partPath := filepath.Join(objdir, uploadID, fmt.Sprintf("%v", p.PartNumber))
|
||||
if p.PartNumber == nil {
|
||||
return nil, s3err.GetAPIError(s3err.ErrInvalidPart)
|
||||
}
|
||||
partPath := filepath.Join(objdir, uploadID, fmt.Sprintf("%v", *p.PartNumber))
|
||||
fi, err := os.Lstat(partPath)
|
||||
if err != nil {
|
||||
return nil, s3err.GetAPIError(s3err.ErrInvalidPart)
|
||||
@@ -178,9 +181,9 @@ func (s *ScoutFS) CompleteMultipartUpload(_ context.Context, input *s3.CompleteM
|
||||
defer f.cleanup()
|
||||
|
||||
for _, p := range parts {
|
||||
pf, err := os.Open(filepath.Join(objdir, uploadID, fmt.Sprintf("%v", p.PartNumber)))
|
||||
pf, err := os.Open(filepath.Join(objdir, uploadID, fmt.Sprintf("%v", *p.PartNumber)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open part %v: %v", p.PartNumber, err)
|
||||
return nil, fmt.Errorf("open part %v: %v", *p.PartNumber, err)
|
||||
}
|
||||
|
||||
// scoutfs move data is a metadata only operation that moves the data
|
||||
@@ -189,7 +192,7 @@ func (s *ScoutFS) CompleteMultipartUpload(_ context.Context, input *s3.CompleteM
|
||||
err = moveData(pf, f.f)
|
||||
pf.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("move blocks part %v: %v", p.PartNumber, err)
|
||||
return nil, fmt.Errorf("move blocks part %v: %v", *p.PartNumber, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,6 +457,13 @@ func (s *ScoutFS) GetObject(_ context.Context, input *s3.GetObjectInput, writer
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objSize := fi.Size()
|
||||
if fi.IsDir() {
|
||||
// directory objects are always 0 len
|
||||
objSize = 0
|
||||
length = 0
|
||||
}
|
||||
|
||||
if length == -1 {
|
||||
length = fi.Size() - startOffset + 1
|
||||
}
|
||||
@@ -462,6 +472,11 @@ func (s *ScoutFS) GetObject(_ context.Context, input *s3.GetObjectInput, writer
|
||||
return nil, s3err.GetAPIError(s3err.ErrInvalidRequest)
|
||||
}
|
||||
|
||||
var contentRange string
|
||||
if acceptRange != "" {
|
||||
contentRange = fmt.Sprintf("bytes %v-%v/%v", startOffset, startOffset+length-1, objSize)
|
||||
}
|
||||
|
||||
if s.glaciermode {
|
||||
// Check if there are any offline exents associated with this file.
|
||||
// If so, we will return the InvalidObjectState error.
|
||||
@@ -519,6 +534,7 @@ func (s *ScoutFS) GetObject(_ context.Context, input *s3.GetObjectInput, writer
|
||||
Metadata: userMetaData,
|
||||
TagCount: &tagCount,
|
||||
StorageClass: types.StorageClassStandard,
|
||||
ContentRange: &contentRange,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
12
go.mod
12
go.mod
@@ -10,10 +10,10 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0
|
||||
github.com/aws/smithy-go v1.20.1
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/gofiber/fiber/v2 v2.52.2
|
||||
github.com/gofiber/fiber/v2 v2.52.3
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/nats-io/nats.go v1.33.1
|
||||
github.com/nats-io/nats.go v1.34.0
|
||||
github.com/pkg/xattr v0.4.9
|
||||
github.com/segmentio/kafka-go v0.4.47
|
||||
github.com/urfave/cli/v2 v2.27.1
|
||||
@@ -26,7 +26,7 @@ require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect
|
||||
@@ -47,9 +47,9 @@ require (
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.8
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.8
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.12
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.9
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.9
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.13
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.4 // indirect
|
||||
|
||||
24
go.sum
24
go.sum
@@ -20,14 +20,14 @@ github.com/aws/aws-sdk-go-v2 v1.26.0 h1:/Ce4OCiM3EkpW7Y+xUnfAFpchU78K7/Ug01sZni9
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.0/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1/go.mod h1:sxpLb+nZk7tIfCWChfd+h4QwHNUR57d8hA1cleTkjJo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.8 h1:0r8epOsiJ7YJz65MGcb8i91ehFp4kvvFe2qkq5oYeRI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.8/go.mod h1:XsmYKxYNuIhLsFddpNds+j9H5XKzjWDdg/SZngiwFio=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.8 h1:WUdNLXbyNbU07V/WFrSOBXqZTDgmmMNMgUFzpYOKJhw=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.8/go.mod h1:iPZzLpaBIfhyvVS/XGD3JvR1GP3YdHTqpySKDlqkfs8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.4 h1:S+L2QSKhUuShih3aq9P/mkzDBiOO5tTyVg+vXREfsfg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.4/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.12 h1:rfAytUY7OgbOMDkzxdiigZkbTe9SDER2dIpO/Fzi9+0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.12/go.mod h1:BaY3WWSgUwV/zq0K3HePyXhRYZxGnDATYERkR0f1RTs=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.9 h1:gRx/NwpNEFSk+yQlgmk1bmxxvQ5TyJ76CWXs9XScTqg=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.9/go.mod h1:dK1FQfpwpql83kbD873E9vz4FyAxuJtR22wzoXn3qq0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.9 h1:N8s0/7yW+h8qR8WaRlPQeJ6czVMNQVNtNdUqf6cItao=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.9/go.mod h1:446YhIdmSV0Jf/SLafGZalQo+xr2iw7/fzXGDPTU1yQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 h1:af5YzcLf80tv4Em4jWVD75lpnOHSBkPUZxZfGkrI3HI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.13 h1:F+PUZee9mlfpEJVZdgyewRumKekS9O3fftj8fEMt0rQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.13/go.mod h1:Rl7i2dEWGHGsBIJCpUxlRt7VwK/HyXxICxdvIRssQHE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 h1:0ScVK/4qZ8CIW0k8jOeFVsyS/sAiXpYxRBLolMkuLQM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4/go.mod h1:84KyjNZdHC6QZW08nfHI6yZgPd+qRgaWcYsyLUo3QY8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 h1:sHmMWWX5E7guWEFQ9SVo6A3S4xpPrWnd77a6y4WM6PU=
|
||||
@@ -65,8 +65,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||
github.com/gofiber/fiber/v2 v2.52.2 h1:b0rYH6b06Df+4NyrbdptQL8ifuxw/Tf2DgfkZkDaxEo=
|
||||
github.com/gofiber/fiber/v2 v2.52.2/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/gofiber/fiber/v2 v2.52.3 h1:bgAZwPv0aHIfRwIUdkWhg6U8D3MEYnoJjT+HfW/dDTo=
|
||||
github.com/gofiber/fiber/v2 v2.52.3/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
@@ -90,8 +90,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/nats-io/nats.go v1.33.1 h1:8TxLZZ/seeEfR97qV0/Bl939tpDnt2Z2fK3HkPypj70=
|
||||
github.com/nats-io/nats.go v1.33.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nats.go v1.34.0 h1:fnxnPCNiwIG5w08rlMcEKTUw4AV/nKyGCOJE8TdhSPk=
|
||||
github.com/nats-io/nats.go v1.34.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
|
||||
@@ -44,6 +44,9 @@ var _ backend.Backend = &BackendMock{}
|
||||
// DeleteBucketFunc: func(contextMoqParam context.Context, deleteBucketInput *s3.DeleteBucketInput) error {
|
||||
// panic("mock out the DeleteBucket method")
|
||||
// },
|
||||
// DeleteBucketPolicyFunc: func(contextMoqParam context.Context, bucket string) error {
|
||||
// panic("mock out the DeleteBucketPolicy method")
|
||||
// },
|
||||
// DeleteBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) error {
|
||||
// panic("mock out the DeleteBucketTagging method")
|
||||
// },
|
||||
@@ -53,7 +56,7 @@ var _ backend.Backend = &BackendMock{}
|
||||
// DeleteObjectTaggingFunc: func(contextMoqParam context.Context, bucket string, object string) error {
|
||||
// panic("mock out the DeleteObjectTagging method")
|
||||
// },
|
||||
// DeleteObjectsFunc: func(contextMoqParam context.Context, deleteObjectsInput *s3.DeleteObjectsInput) (s3response.DeleteObjectsResult, error) {
|
||||
// DeleteObjectsFunc: func(contextMoqParam context.Context, deleteObjectsInput *s3.DeleteObjectsInput) (s3response.DeleteResult, error) {
|
||||
// panic("mock out the DeleteObjects method")
|
||||
// },
|
||||
// GetBucketAclFunc: func(contextMoqParam context.Context, getBucketAclInput *s3.GetBucketAclInput) ([]byte, error) {
|
||||
@@ -174,6 +177,9 @@ type BackendMock struct {
|
||||
// DeleteBucketFunc mocks the DeleteBucket method.
|
||||
DeleteBucketFunc func(contextMoqParam context.Context, deleteBucketInput *s3.DeleteBucketInput) error
|
||||
|
||||
// DeleteBucketPolicyFunc mocks the DeleteBucketPolicy method.
|
||||
DeleteBucketPolicyFunc func(contextMoqParam context.Context, bucket string) error
|
||||
|
||||
// DeleteBucketTaggingFunc mocks the DeleteBucketTagging method.
|
||||
DeleteBucketTaggingFunc func(contextMoqParam context.Context, bucket string) error
|
||||
|
||||
@@ -331,6 +337,13 @@ type BackendMock struct {
|
||||
// DeleteBucketInput is the deleteBucketInput argument value.
|
||||
DeleteBucketInput *s3.DeleteBucketInput
|
||||
}
|
||||
// DeleteBucketPolicy holds details about calls to the DeleteBucketPolicy method.
|
||||
DeleteBucketPolicy []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
ContextMoqParam context.Context
|
||||
// Bucket is the bucket argument value.
|
||||
Bucket string
|
||||
}
|
||||
// DeleteBucketTagging holds details about calls to the DeleteBucketTagging method.
|
||||
DeleteBucketTagging []struct {
|
||||
// ContextMoqParam is the contextMoqParam argument value.
|
||||
@@ -585,6 +598,7 @@ type BackendMock struct {
|
||||
lockCreateBucket sync.RWMutex
|
||||
lockCreateMultipartUpload sync.RWMutex
|
||||
lockDeleteBucket sync.RWMutex
|
||||
lockDeleteBucketPolicy sync.RWMutex
|
||||
lockDeleteBucketTagging sync.RWMutex
|
||||
lockDeleteObject sync.RWMutex
|
||||
lockDeleteObjectTagging sync.RWMutex
|
||||
@@ -881,6 +895,42 @@ func (mock *BackendMock) DeleteBucketCalls() []struct {
|
||||
return calls
|
||||
}
|
||||
|
||||
// DeleteBucketPolicy calls DeleteBucketPolicyFunc.
|
||||
func (mock *BackendMock) DeleteBucketPolicy(contextMoqParam context.Context, bucket string) error {
|
||||
if mock.DeleteBucketPolicyFunc == nil {
|
||||
panic("BackendMock.DeleteBucketPolicyFunc: method is nil but Backend.DeleteBucketPolicy was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
ContextMoqParam context.Context
|
||||
Bucket string
|
||||
}{
|
||||
ContextMoqParam: contextMoqParam,
|
||||
Bucket: bucket,
|
||||
}
|
||||
mock.lockDeleteBucketPolicy.Lock()
|
||||
mock.calls.DeleteBucketPolicy = append(mock.calls.DeleteBucketPolicy, callInfo)
|
||||
mock.lockDeleteBucketPolicy.Unlock()
|
||||
return mock.DeleteBucketPolicyFunc(contextMoqParam, bucket)
|
||||
}
|
||||
|
||||
// DeleteBucketPolicyCalls gets all the calls that were made to DeleteBucketPolicy.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedBackend.DeleteBucketPolicyCalls())
|
||||
func (mock *BackendMock) DeleteBucketPolicyCalls() []struct {
|
||||
ContextMoqParam context.Context
|
||||
Bucket string
|
||||
} {
|
||||
var calls []struct {
|
||||
ContextMoqParam context.Context
|
||||
Bucket string
|
||||
}
|
||||
mock.lockDeleteBucketPolicy.RLock()
|
||||
calls = mock.calls.DeleteBucketPolicy
|
||||
mock.lockDeleteBucketPolicy.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// DeleteBucketTagging calls DeleteBucketTaggingFunc.
|
||||
func (mock *BackendMock) DeleteBucketTagging(contextMoqParam context.Context, bucket string) error {
|
||||
if mock.DeleteBucketTaggingFunc == nil {
|
||||
|
||||
@@ -80,7 +80,15 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("tagging") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectTaggingAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -139,7 +147,15 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.ListMultipartUploadPartsAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -169,7 +185,15 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("acl") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ_ACP", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionReadAcp,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAclAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -191,7 +215,15 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if attrs := ctx.Get("X-Amz-Object-Attributes"); attrs != "" {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAttributesAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -218,7 +250,15 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ_ACP", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -341,7 +381,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("tagging") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketTaggingAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -378,7 +425,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("versioning") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketVersioningAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -407,7 +461,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("policy") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketPolicyAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -427,7 +488,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("versions") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketVersionsAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -464,7 +532,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("acl") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ_ACP", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionReadAcp,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.GetBucketAclAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -490,7 +565,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("uploads") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketMultipartUploadsAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -525,7 +607,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.QueryInt("list-type") == 2 {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -560,7 +649,14 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -640,7 +736,14 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
tags[tag.Key] = tag.Value
|
||||
}
|
||||
|
||||
err = auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err = auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -661,7 +764,14 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("versioning") {
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketVersioningAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -698,7 +808,14 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("policy") {
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketPolicyAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -708,6 +825,17 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = auth.ValidatePolicyDocument(ctx.Body(), bucket, c.iam)
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "PutBucketPolicy",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
err = c.be.PutBucketPolicy(ctx.Context(), bucket, ctx.Body())
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -724,7 +852,14 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
var accessControlPolicy auth.AccessControlPolicy
|
||||
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE_ACP", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWriteAcp,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketAclAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -844,9 +979,7 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
defACL := auth.ACL{
|
||||
ACL: "private",
|
||||
Owner: acct.Access,
|
||||
Grantees: []auth.Grantee{},
|
||||
Owner: acct.Access,
|
||||
}
|
||||
|
||||
updAcl, err := auth.UpdateACL(&s3.PutBucketAclInput{
|
||||
@@ -950,7 +1083,15 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
tags[tag.Key] = tag.Value
|
||||
}
|
||||
|
||||
err = auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err = auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: keyStart,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -984,6 +1125,24 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyObjectCopyAccess(ctx.Context(), c.be, copySource, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: keyStart,
|
||||
Action: auth.PutObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "UploadPartCopy",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
resp, err := c.be.UploadPartCopy(ctx.Context(), &s3.UploadPartCopyInput{
|
||||
Bucket: &bucket,
|
||||
Key: &keyStart,
|
||||
@@ -1013,7 +1172,15 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: keyStart,
|
||||
Action: auth.PutObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1152,7 +1319,15 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if copySource != "" {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyObjectCopyAccess(ctx.Context(), c.be, copySource, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: keyStart,
|
||||
Action: auth.PutObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -1225,7 +1400,15 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||
|
||||
metadata := utils.GetUserMetaData(&ctx.Request().Header)
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: keyStart,
|
||||
Action: auth.PutObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1281,7 +1464,14 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error {
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("tagging") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.PutBucketTaggingAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1301,7 +1491,42 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
if ctx.Request().URI().QueryArgs().Has("policy") {
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteBucketPolicyAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "DeleteBucketPolicy",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
err = c.be.DeleteBucketPolicy(ctx.Context(), bucket)
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "DeleteBucketPolicy",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
Status: http.StatusNoContent,
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteBucketAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1340,7 +1565,14 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err = auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.DeleteObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1380,12 +1612,20 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if ctx.Request().URI().QueryArgs().Has("tagging") {
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.DeleteObjectTaggingAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
Logger: c.logger,
|
||||
Action: "RemoveObjectTagging",
|
||||
Action: "DeleteObjectTagging",
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
})
|
||||
}
|
||||
@@ -1405,7 +1645,15 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
expectedBucketOwner := ctx.Get("X-Amz-Expected-Bucket-Owner")
|
||||
requestPayer := ctx.Get("X-Amz-Request-Payer")
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.AbortMultipartUploadAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1432,7 +1680,15 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.DeleteObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1465,7 +1721,14 @@ func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) error {
|
||||
isRoot := ctx.Locals("isRoot").(bool)
|
||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Action: auth.ListBucketAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1502,7 +1765,15 @@ func (c S3ApiController) HeadObject(ctx *fiber.Ctx) error {
|
||||
key = strings.Join([]string{key, keyEnd}, "/")
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1608,7 +1879,15 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err = auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.RestoreObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendResponse(ctx, err,
|
||||
&MetaOpts{
|
||||
@@ -1646,7 +1925,15 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot)
|
||||
err = auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionRead,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.GetObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -1688,7 +1975,15 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
@@ -1727,7 +2022,15 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot)
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Acl: parsedAcl,
|
||||
AclPermission: types.PermissionWrite,
|
||||
IsRoot: isRoot,
|
||||
Acc: acct,
|
||||
Bucket: bucket,
|
||||
Object: key,
|
||||
Action: auth.PutObjectAction,
|
||||
})
|
||||
if err != nil {
|
||||
return SendXMLResponse(ctx, nil, err,
|
||||
&MetaOpts{
|
||||
|
||||
@@ -570,6 +570,19 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
||||
</VersioningConfiguration>
|
||||
`
|
||||
|
||||
policyBody := `
|
||||
{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::my-bucket/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
s3ApiController := S3ApiController{
|
||||
be: &BackendMock{
|
||||
GetBucketAclFunc: func(context.Context, *s3.GetBucketAclInput) ([]byte, error) {
|
||||
@@ -667,12 +680,21 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
||||
statusCode: 200,
|
||||
},
|
||||
{
|
||||
name: "Put-bucket-policy-success",
|
||||
name: "Put-bucket-policy-invalid-body",
|
||||
app: app,
|
||||
args: args{
|
||||
req: httptest.NewRequest(http.MethodPut, "/my-bucket?policy", nil),
|
||||
},
|
||||
wantErr: false,
|
||||
statusCode: 400,
|
||||
},
|
||||
{
|
||||
name: "Put-bucket-policy-success",
|
||||
app: app,
|
||||
args: args{
|
||||
req: httptest.NewRequest(http.MethodPut, "/my-bucket?policy", strings.NewReader(policyBody)),
|
||||
},
|
||||
wantErr: false,
|
||||
statusCode: 200,
|
||||
},
|
||||
{
|
||||
|
||||
14
tests/.env.s3.default
Normal file
14
tests/.env.s3.default
Normal file
@@ -0,0 +1,14 @@
|
||||
AWS_PROFILE=versity_s3
|
||||
AWS_ENDPOINT_URL=https://127.0.0.1:7070
|
||||
VERSITY_EXE=./versitygw
|
||||
RUN_VERSITYGW=true
|
||||
BACKEND=s3
|
||||
LOCAL_FOLDER=/tmp/gw
|
||||
BUCKET_ONE_NAME=versity-gwtest-bucket-one
|
||||
BUCKET_TWO_NAME=versity-gwtest-bucket-two
|
||||
#RECREATE_BUCKETS=true
|
||||
CERT=$PWD/cert.pem
|
||||
KEY=$PWD/versitygw.pem
|
||||
S3CMD_CONFIG=./tests/s3cfg.local.default
|
||||
SECRETS_FILE=./tests/.secrets.s3
|
||||
MC_ALIAS=versity
|
||||
@@ -242,6 +242,41 @@ func TestGetBucketAcl(s *S3Conf) {
|
||||
GetBucketAcl_success(s)
|
||||
}
|
||||
|
||||
func TestPutBucketPolicy(s *S3Conf) {
|
||||
PutBucketPolicy_non_existing_bucket(s)
|
||||
PutBucketPolicy_invalid_effect(s)
|
||||
PutBucketPolicy_empty_actions_string(s)
|
||||
PutBucketPolicy_empty_actions_array(s)
|
||||
PutBucketPolicy_invalid_action(s)
|
||||
PutBucketPolicy_unsupported_action(s)
|
||||
PutBucketPolicy_incorrect_action_wildcard_usage(s)
|
||||
PutBucketPolicy_empty_principals_string(s)
|
||||
PutBucketPolicy_empty_principals_array(s)
|
||||
PutBucketPolicy_principals_incorrect_wildcard_usage(s)
|
||||
PutBucketPolicy_non_existing_principals(s)
|
||||
PutBucketPolicy_empty_resources_string(s)
|
||||
PutBucketPolicy_empty_resources_array(s)
|
||||
PutBucketPolicy_invalid_resource_prefix(s)
|
||||
PutBucketPolicy_invalid_resource_with_starting_slash(s)
|
||||
PutBucketPolicy_duplicate_resource(s)
|
||||
PutBucketPolicy_incorrect_bucket_name(s)
|
||||
PutBucketPolicy_object_action_on_bucket_resource(s)
|
||||
PutBucketPolicy_bucket_action_on_object_resource(s)
|
||||
PutBucketPolicy_success(s)
|
||||
}
|
||||
|
||||
func TestGetBucketPolicy(s *S3Conf) {
|
||||
GetBucketPolicy_non_existing_bucket(s)
|
||||
GetBucketPolicy_default_empty_policy(s)
|
||||
GetBucketPolicy_success(s)
|
||||
}
|
||||
|
||||
func TestDeleteBucketPolicy(s *S3Conf) {
|
||||
DeleteBucketPolicy_non_existing_bucket(s)
|
||||
DeleteBucketPolicy_remove_before_setting(s)
|
||||
DeleteBucketPolicy_success(s)
|
||||
}
|
||||
|
||||
func TestFullFlow(s *S3Conf) {
|
||||
TestAuthentication(s)
|
||||
TestPresignedAuthentication(s)
|
||||
@@ -271,6 +306,9 @@ func TestFullFlow(s *S3Conf) {
|
||||
TestCompleteMultipartUpload(s)
|
||||
TestPutBucketAcl(s)
|
||||
TestGetBucketAcl(s)
|
||||
TestPutBucketPolicy(s)
|
||||
TestGetBucketPolicy(s)
|
||||
TestDeleteBucketPolicy(s)
|
||||
}
|
||||
|
||||
func TestPosix(s *S3Conf) {
|
||||
@@ -445,6 +483,32 @@ func GetIntTests() IntTests {
|
||||
"GetBucketAcl_non_existing_bucket": GetBucketAcl_non_existing_bucket,
|
||||
"GetBucketAcl_access_denied": GetBucketAcl_access_denied,
|
||||
"GetBucketAcl_success": GetBucketAcl_success,
|
||||
"PutBucketPolicy_non_existing_bucket": PutBucketPolicy_non_existing_bucket,
|
||||
"PutBucketPolicy_invalid_effect": PutBucketPolicy_invalid_effect,
|
||||
"PutBucketPolicy_empty_actions_string": PutBucketPolicy_empty_actions_string,
|
||||
"PutBucketPolicy_empty_actions_array": PutBucketPolicy_empty_actions_array,
|
||||
"PutBucketPolicy_invalid_action": PutBucketPolicy_invalid_action,
|
||||
"PutBucketPolicy_unsupported_action": PutBucketPolicy_unsupported_action,
|
||||
"PutBucketPolicy_incorrect_action_wildcard_usage": PutBucketPolicy_incorrect_action_wildcard_usage,
|
||||
"PutBucketPolicy_empty_principals_string": PutBucketPolicy_empty_principals_string,
|
||||
"PutBucketPolicy_empty_principals_array": PutBucketPolicy_empty_principals_array,
|
||||
"PutBucketPolicy_principals_incorrect_wildcard_usage": PutBucketPolicy_principals_incorrect_wildcard_usage,
|
||||
"PutBucketPolicy_non_existing_principals": PutBucketPolicy_non_existing_principals,
|
||||
"PutBucketPolicy_empty_resources_string": PutBucketPolicy_empty_resources_string,
|
||||
"PutBucketPolicy_empty_resources_array": PutBucketPolicy_empty_resources_array,
|
||||
"PutBucketPolicy_invalid_resource_prefix": PutBucketPolicy_invalid_resource_prefix,
|
||||
"PutBucketPolicy_invalid_resource_with_starting_slash": PutBucketPolicy_invalid_resource_with_starting_slash,
|
||||
"PutBucketPolicy_duplicate_resource": PutBucketPolicy_duplicate_resource,
|
||||
"PutBucketPolicy_incorrect_bucket_name": PutBucketPolicy_incorrect_bucket_name,
|
||||
"PutBucketPolicy_object_action_on_bucket_resource": PutBucketPolicy_object_action_on_bucket_resource,
|
||||
"PutBucketPolicy_bucket_action_on_object_resource": PutBucketPolicy_bucket_action_on_object_resource,
|
||||
"PutBucketPolicy_success": PutBucketPolicy_success,
|
||||
"GetBucketPolicy_non_existing_bucket": GetBucketPolicy_non_existing_bucket,
|
||||
"GetBucketPolicy_default_empty_policy": GetBucketPolicy_default_empty_policy,
|
||||
"GetBucketPolicy_success": GetBucketPolicy_success,
|
||||
"DeleteBucketPolicy_non_existing_bucket": DeleteBucketPolicy_non_existing_bucket,
|
||||
"DeleteBucketPolicy_remove_before_setting": DeleteBucketPolicy_remove_before_setting,
|
||||
"DeleteBucketPolicy_success": DeleteBucketPolicy_success,
|
||||
"PutObject_overwrite_dir_obj": PutObject_overwrite_dir_obj,
|
||||
"PutObject_overwrite_file_obj": PutObject_overwrite_file_obj,
|
||||
"PutObject_dir_obj_with_data": PutObject_dir_obj_with_data,
|
||||
|
||||
@@ -3265,28 +3265,35 @@ func CopyObject_not_owned_source_bucket(s *S3Conf) error {
|
||||
}
|
||||
|
||||
usr := user{
|
||||
access: "admin1",
|
||||
secret: "admin1secret",
|
||||
role: "admin",
|
||||
access: "grt1",
|
||||
secret: "grt1secret",
|
||||
role: "user",
|
||||
}
|
||||
|
||||
cfg := *s
|
||||
cfg.awsID = usr.access
|
||||
cfg.awsSecret = usr.secret
|
||||
|
||||
userS3Client := s3.NewFromConfig(cfg.Config())
|
||||
|
||||
err = createUsers(s, []user{usr})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstBucket := getBucketName()
|
||||
err = setup(&cfg, dstBucket)
|
||||
err = setup(s, dstBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = changeBucketsOwner(s, []string{bucket}, usr.access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
_, err = userS3Client.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &dstBucket,
|
||||
Key: getPtr("obj-1"),
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v", bucket, srcObj)),
|
||||
@@ -5241,6 +5248,555 @@ func GetBucketAcl_success(s *S3Conf) error {
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_non_existing_bucket(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_non_existing_bucket"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
doc := genPolicyDoc("Allow", `"*"`, `"s3:*"`, fmt.Sprintf(`"arn:aws:s3:::%v"`, bucket))
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: getPtr("non_existing_bucket"),
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_invalid_effect(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_invalid_effect"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("invalid_effect", `"*"`, `"s3:*"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("invalid effect: invalid_effect")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_empty_actions_string(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_empty_actions_string"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `""`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("actions can't be empty")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_empty_actions_array(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_empty_actions_array"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `[]`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("actions can't be empty")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_invalid_action(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_invalid_action"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `"ListObjects"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("invalid action: ListObjects")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_unsupported_action(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_unsupported_action"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `"s3:PutLifecycleConfiguration"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("unsupported action: s3:PutLifecycleConfiguration")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_incorrect_action_wildcard_usage(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_incorrect_action_wildcard_usage"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `"s3:hello*"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("invalid wildcard usage: s3:hello prefix is not in the supported actions list")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_empty_principals_string(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_empty_principals_string"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `""`, `"s3:*"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("principals can't be empty")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_empty_principals_array(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_empty_principals_array"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `[]`, `"s3:*"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("principals can't be empty")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_principals_incorrect_wildcard_usage(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_principals_incorrect_wildcard_usage"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `["*", "grt1"]`, `"s3:*"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("principals should either contain * or user access keys")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_non_existing_principals(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_non_existing_principals"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `["a_rarely_existing_user_account_1", "a_rarely_existing_user_account_2"]`, `"s3:*"`, `"arn:aws:s3:::*"`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
apiErr1 := getMalformedPolicyError(fmt.Sprintf("user accounts don't exist: %v", []string{"a_rarely_existing_user_account_1", "a_rarely_existing_user_account_2"}))
|
||||
apiErr2 := getMalformedPolicyError(fmt.Sprintf("user accounts don't exist: %v", []string{"a_rarely_existing_user_account_2", "a_rarely_existing_user_account_1"}))
|
||||
|
||||
err1 := checkApiErr(err, apiErr1)
|
||||
err2 := checkApiErr(err, apiErr2)
|
||||
|
||||
if err1 != nil && err2 != nil {
|
||||
return err1
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_empty_resources_string(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_empty_resources_string"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:*"`, `""`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("resources can't be empty")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_empty_resources_array(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_empty_resources_array"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:*"`, `[]`)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("resources can't be empty")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_invalid_resource_prefix(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_invalid_resource_prefix"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
resource := fmt.Sprintf(`"arn:aws:iam:::%v"`, bucket)
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:*"`, resource)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError(fmt.Sprintf("invalid resource: %v", resource[1:len(resource)-1]))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_invalid_resource_with_starting_slash(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_invalid_resource_with_starting_slash"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
resource := fmt.Sprintf(`"arn:aws:s3:::/%v"`, bucket)
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:*"`, resource)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError(fmt.Sprintf("invalid resource: %v", resource[1:len(resource)-1]))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_duplicate_resource(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_duplicate_resource"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
resource := fmt.Sprintf(`"arn:aws:s3:::%v"`, bucket)
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:*"`, fmt.Sprintf("[%v, %v]", resource, resource))
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError(fmt.Sprintf("duplicate resource: %v", resource[1:len(resource)-1]))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_incorrect_bucket_name(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_incorrect_bucket_name"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
resource := fmt.Sprintf(`"arn:aws:s3:::prefix-%v"`, bucket)
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:*"`, resource)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError(fmt.Sprintf("incorrect bucket name in prefix-%v", bucket))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_object_action_on_bucket_resource(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_object_action_on_bucket_resource"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
resource := fmt.Sprintf(`"arn:aws:s3:::%v"`, bucket)
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:PutObjectTagging"`, resource)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("unsupported object action 's3:PutObjectTagging' on the specified resources")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_bucket_action_on_object_resource(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_object_action_on_bucket_resource"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
resource := fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)
|
||||
doc := genPolicyDoc("Allow", `["*"]`, `"s3:DeleteBucket"`, resource)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, getMalformedPolicyError("unsupported bucket action 's3:DeleteBucket' on the specified resources")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutBucketPolicy_success(s *S3Conf) error {
|
||||
testName := "PutBucketPolicy_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
err := createUsers(s, []user{
|
||||
{"grt1", "grt1secret", "user"},
|
||||
{"grt2", "grt2secret", "user"},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bucketResource := fmt.Sprintf(`"arn:aws:s3:::%v"`, bucket)
|
||||
objectResource := fmt.Sprintf(`"arn:aws:s3:::%v/*"`, bucket)
|
||||
|
||||
for _, doc := range []string{
|
||||
genPolicyDoc("Allow", `["grt1", "grt2"]`, `["s3:DeleteBucket", "s3:GetBucketAcl"]`, bucketResource),
|
||||
genPolicyDoc("Deny", `"*"`, `"s3:DeleteBucket"`, fmt.Sprintf(`"arn:aws:s3:::%v"`, bucket)),
|
||||
genPolicyDoc("Allow", `"grt1"`, `["s3:PutBucketVersioning", "s3:ListMultipartUploadParts", "s3:ListBucket"]`, fmt.Sprintf(`[%v, %v]`, bucketResource, objectResource)),
|
||||
genPolicyDoc("Allow", `"*"`, `"s3:*"`, fmt.Sprintf(`[%v, %v]`, bucketResource, objectResource)),
|
||||
genPolicyDoc("Allow", `"*"`, `"s3:Get*"`, objectResource),
|
||||
genPolicyDoc("Deny", `"*"`, `"s3:Create*"`, fmt.Sprintf(`[%v, %v]`, bucketResource, objectResource)),
|
||||
} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetBucketPolicy_non_existing_bucket(s *S3Conf) error {
|
||||
testName := "GetBucketPolicy_non_existing_bucket"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.GetBucketPolicy(ctx, &s3.GetBucketPolicyInput{
|
||||
Bucket: getPtr("non_existing_bucket"),
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetBucketPolicy_default_empty_policy(s *S3Conf) error {
|
||||
testName := "GetBucketPolicy_default_empty_policy"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.GetBucketPolicy(ctx, &s3.GetBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if out.Policy != nil {
|
||||
return fmt.Errorf("expected policy to be nil, instead got %s", *out.Policy)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func GetBucketPolicy_success(s *S3Conf) error {
|
||||
testName := "GetBucketPolicy_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `["s3:DeleteBucket", "s3:GetBucketTagging"]`, fmt.Sprintf(`"arn:aws:s3:::%v"`, bucket))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.GetBucketPolicy(ctx, &s3.GetBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if out.Policy == nil {
|
||||
return fmt.Errorf("expected non nil policy result")
|
||||
}
|
||||
|
||||
if *out.Policy != doc {
|
||||
return fmt.Errorf("expected the bucket policy to be %v, instead got %v", doc, *out.Policy)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteBucketPolicy_non_existing_bucket(s *S3Conf) error {
|
||||
testName := "DeleteBucketPolicy_non_existing_bucket"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.DeleteBucketPolicy(ctx, &s3.DeleteBucketPolicyInput{
|
||||
Bucket: getPtr("non_existing_bucket"),
|
||||
})
|
||||
cancel()
|
||||
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteBucketPolicy_remove_before_setting(s *S3Conf) error {
|
||||
testName := "DeleteBucketPolicy_remove_before_setting"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.DeleteBucketPolicy(ctx, &s3.DeleteBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteBucketPolicy_success(s *S3Conf) error {
|
||||
testName := "DeleteBucketPolicy_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `["s3:DeleteBucket", "s3:GetBucketTagging"]`, fmt.Sprintf(`"arn:aws:s3:::%v"`, bucket))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err := s3client.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
Policy: &doc,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteBucketPolicy(ctx, &s3.DeleteBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
out, err := s3client.GetBucketPolicy(ctx, &s3.GetBucketPolicyInput{
|
||||
Bucket: &bucket,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if out.Policy != nil {
|
||||
return fmt.Errorf("expected policy to be nil, instead got %s", *out.Policy)
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// IAM related tests
|
||||
// multi-user iam tests
|
||||
func IAM_user_access_denied(s *S3Conf) error {
|
||||
|
||||
@@ -219,12 +219,17 @@ func checkApiErr(err error, apiErr s3err.APIError) error {
|
||||
}
|
||||
var ae smithy.APIError
|
||||
if errors.As(err, &ae) {
|
||||
if ae.ErrorCode() == apiErr.Code && ae.ErrorMessage() == apiErr.Description {
|
||||
return nil
|
||||
if ae.ErrorCode() != apiErr.Code {
|
||||
return fmt.Errorf("expected error code to be %v, instead got %v", apiErr.Code, ae.ErrorCode())
|
||||
}
|
||||
|
||||
return fmt.Errorf("expected %v, instead got %v", apiErr.Code, ae.ErrorCode())
|
||||
if ae.ErrorMessage() != apiErr.Description {
|
||||
return fmt.Errorf("expected error message to be %v, instead got %v", apiErr.Description, ae.ErrorMessage())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("expected aws api error, instead got: %w", err)
|
||||
}
|
||||
|
||||
@@ -603,3 +608,28 @@ func changeAuthCred(uri, newVal string, index int) (string, error) {
|
||||
|
||||
return urlParsed.String(), nil
|
||||
}
|
||||
|
||||
func genPolicyDoc(effect, principal, action, resource string) string {
|
||||
jsonTemplate := `
|
||||
{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "%s",
|
||||
"Principal": %s,
|
||||
"Action": %s,
|
||||
"Resource": %s
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
return fmt.Sprintf(jsonTemplate, effect, principal, action, resource)
|
||||
}
|
||||
|
||||
func getMalformedPolicyError(msg string) s3err.APIError {
|
||||
return s3err.APIError{
|
||||
Code: "MalformedPolicy",
|
||||
Description: msg,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ host_base = 127.0.0.1:7070
|
||||
host_bucket = 127.0.0.1:7070
|
||||
bucket_location = us-east-1
|
||||
use_https = True
|
||||
signurl_use_https = True
|
||||
|
||||
# Enable S3 v4 signature APIs
|
||||
signature_v2 = False
|
||||
|
||||
@@ -315,3 +315,7 @@ source ./tests/test_common.sh
|
||||
delete_bucket_or_contents "aws" "$BUCKET_ONE_NAME"
|
||||
delete_test_files $bucket_file
|
||||
}
|
||||
|
||||
@test "test-presigned-url-utf8-chars" {
|
||||
test_common_presigned_url_utf8_chars "aws"
|
||||
}
|
||||
|
||||
@@ -256,3 +256,40 @@ test_common_multipart_upload() {
|
||||
delete_bucket_or_contents "$1" "$BUCKET_ONE_NAME"
|
||||
delete_test_files $bucket_file
|
||||
}
|
||||
|
||||
test_common_presigned_url_utf8_chars() {
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "presigned url command missing command type"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local bucket_file="my-$%^&*;"
|
||||
local bucket_file_copy="bucket-file-copy"
|
||||
bucket_file_data="test file\n"
|
||||
|
||||
create_test_files "$bucket_file" || local created=$?
|
||||
printf "%s" "$bucket_file_data" > "$test_file_folder"/"$bucket_file"
|
||||
setup_bucket "$1" "$BUCKET_ONE_NAME" || local result=$?
|
||||
[[ $result -eq 0 ]] || fail "Failed to create bucket '$BUCKET_ONE_NAME'"
|
||||
|
||||
put_object "$1" "$test_file_folder"/"$bucket_file" "$BUCKET_ONE_NAME"/"$bucket_file" || put_result=$?
|
||||
[[ $put_result -eq 0 ]] || fail "Failed to add object $bucket_file"
|
||||
|
||||
create_presigned_url "$1" "$BUCKET_ONE_NAME" "$bucket_file" || presigned_result=$?
|
||||
[[ $presigned_result -eq 0 ]] || fail "presigned url creation failure"
|
||||
|
||||
error=$(curl -k -v "$presigned_url" -o "$test_file_folder"/"$bucket_file_copy") || curl_result=$?
|
||||
if [[ $curl_result -ne 0 ]]; then
|
||||
fail "error downloading file with curl: $error"
|
||||
fi
|
||||
compare_files "$test_file_folder"/"$bucket_file" "$test_file_folder"/"$bucket_file_copy" || compare_result=$?
|
||||
if [[ $compare_result -ne 0 ]]; then
|
||||
echo "file one: $(cat "$test_file_folder"/"$bucket_file")"
|
||||
echo "file two: $(cat "$test_file_folder"/"$bucket_file_copy")"
|
||||
fail "files don't match"
|
||||
fi
|
||||
|
||||
delete_bucket_or_contents "$1" "$BUCKET_ONE_NAME"
|
||||
delete_test_files "$bucket_file" "$bucket_file_copy"
|
||||
}
|
||||
|
||||
@@ -37,3 +37,7 @@ export RUN_MC=true
|
||||
@test "test_multipart_upload_mc" {
|
||||
test_common_multipart_upload "mc"
|
||||
}
|
||||
|
||||
@test "test_presigned_url_utf8_chars_mc" {
|
||||
test_common_presigned_url_utf8_chars "mc"
|
||||
}
|
||||
|
||||
@@ -31,4 +31,8 @@ export RUN_S3CMD=true
|
||||
|
||||
@test "test_multipart_upload_s3cmd" {
|
||||
test_common_multipart_upload "s3cmd"
|
||||
}
|
||||
}
|
||||
|
||||
#@test "test_presigned_url_utf8_chars_s3cmd" {
|
||||
# test_common_presigned_url_utf8_chars "s3cmd"
|
||||
#}
|
||||
@@ -901,3 +901,28 @@ upload_part_copy() {
|
||||
etag=$(echo "$etag_json" | jq '.CopyPartResult.ETag')
|
||||
export etag
|
||||
}
|
||||
|
||||
create_presigned_url() {
|
||||
if [[ $# -ne 3 ]]; then
|
||||
echo "create presigned url function requires command type, bucket, and filename"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local presign_result=0
|
||||
if [[ $1 == 'aws' ]]; then
|
||||
presigned_url=$(aws s3 presign "s3://$2/$3" --expires-in 900) || presign_result=$?
|
||||
elif [[ $1 == 's3cmd' ]]; then
|
||||
presigned_url=$(s3cmd --no-check-certificate "${S3CMD_OPTS[@]}" signurl "s3://$2/$3" "$(echo "$(date +%s)" + 900 | bc)") || presign_result=$?
|
||||
elif [[ $1 == 'mc' ]]; then
|
||||
presigned_url_data=$(mc --insecure share download --recursive "$MC_ALIAS/$2/$3") || presign_result=$?
|
||||
presigned_url="${presigned_url_data#*Share: }"
|
||||
else
|
||||
echo "unrecognized command type $1"
|
||||
return 1
|
||||
fi
|
||||
if [[ $presign_result -ne 0 ]]; then
|
||||
echo "error generating presigned url: $presigned_url"
|
||||
return 1
|
||||
fi
|
||||
export presigned_url
|
||||
}
|
||||
|
||||
180
tests/versity.sh
180
tests/versity.sh
@@ -1,5 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
source ./tests/util_file.sh
|
||||
|
||||
check_exe_params_versity() {
|
||||
if [ -z "$LOCAL_FOLDER" ]; then
|
||||
echo "No local storage folder set"
|
||||
return 1
|
||||
elif [ -z "$VERSITY_EXE" ]; then
|
||||
echo "No versity executable location set"
|
||||
return 1
|
||||
elif [ -z "$BACKEND" ]; then
|
||||
echo "No backend parameter set (options: 'posix')"
|
||||
return 1
|
||||
fi
|
||||
if [ "$BACKEND" == 's3' ]; then
|
||||
if [ -z "$AWS_ACCESS_KEY_ID_TWO" ]; then
|
||||
echo "missing second AWS access key ID for s3 backend"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$AWS_SECRET_ACCESS_KEY_TWO" ]; then
|
||||
echo "missing second AWS secret access key for s3 backend"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_exe_params() {
|
||||
if [ -z "$AWS_ACCESS_KEY_ID" ]; then
|
||||
echo "No AWS access key set"
|
||||
@@ -21,14 +46,9 @@ check_exe_params() {
|
||||
return 1
|
||||
fi
|
||||
if [[ $RUN_VERSITYGW == "true" ]]; then
|
||||
if [ -z "$LOCAL_FOLDER" ]; then
|
||||
echo "No local storage folder set"
|
||||
return 1
|
||||
elif [ -z "$VERSITY_EXE" ]; then
|
||||
echo "No versity executable location set"
|
||||
return 1
|
||||
elif [ -z "$BACKEND" ]; then
|
||||
echo "No backend parameter set (options: 'posix')"
|
||||
local check_result
|
||||
check_exe_params_versity || check_result=$?
|
||||
if [[ $check_result -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
@@ -59,32 +79,142 @@ start_versity() {
|
||||
fi
|
||||
|
||||
if [ "$RUN_VERSITYGW" == "true" ]; then
|
||||
base_command="ROOT_ACCESS_KEY=$AWS_ACCESS_KEY_ID ROOT_SECRET_KEY=$AWS_SECRET_ACCESS_KEY VGW_REGION=$AWS_REGION $VERSITY_EXE"
|
||||
if [ -n "$CERT" ] && [ -n "$KEY" ]; then
|
||||
base_command+=" --cert $CERT --key $KEY"
|
||||
run_versity_app || run_result=$?
|
||||
if [[ $run_result -ne 0 ]]; then
|
||||
echo "error starting versity apps"
|
||||
return 1
|
||||
fi
|
||||
base_command+=" $BACKEND $LOCAL_FOLDER &"
|
||||
eval "$base_command"
|
||||
versitygw_pid=$!
|
||||
|
||||
export versitygw_pid VERSITY_EXE BACKEND LOCAL_FOLDER
|
||||
fi
|
||||
|
||||
export versitygw_pid AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION AWS_PROFILE AWS_ENDPOINT_URL
|
||||
export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION AWS_PROFILE AWS_ENDPOINT_URL
|
||||
}
|
||||
|
||||
start_versity_process() {
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "start versity process function requires number"
|
||||
return 1
|
||||
fi
|
||||
create_test_file_folder || create_result=$?
|
||||
if [[ $create_result -ne 0 ]]; then
|
||||
echo "error creating test log folder"
|
||||
return 1
|
||||
fi
|
||||
base_command+=(">" "$test_file_folder/versity_log_$1.txt" "2>&1")
|
||||
("${base_command[@]}") &
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "error running versitygw command: $(cat "$test_file_folder/versity_log_$1.txt")"
|
||||
return 1
|
||||
fi
|
||||
eval versitygw_pid_"$1"=$!
|
||||
local pid
|
||||
eval pid=\$versitygw_pid_"$1"
|
||||
sleep 1
|
||||
|
||||
local proc_check
|
||||
check_result=$(kill -0 $pid 2>&1) || proc_check=$?
|
||||
if [[ $proc_check -ne 0 ]]; then
|
||||
echo "versitygw failed to start: $check_result"
|
||||
echo "log data: $(cat "$test_file_folder/versity_log_$1.txt")"
|
||||
return 1
|
||||
fi
|
||||
export versitygw_pid_"$1"
|
||||
}
|
||||
|
||||
run_versity_app_posix() {
|
||||
if [[ $# -ne 3 ]]; then
|
||||
echo "run versity app w/posix command requires access ID, secret key, process number"
|
||||
return 1
|
||||
fi
|
||||
base_command=("$VERSITY_EXE" --access="$1" --secret="$2" --region="$AWS_REGION")
|
||||
if [ -n "$CERT" ] && [ -n "$KEY" ]; then
|
||||
base_command+=(--cert "$CERT" --key "$KEY")
|
||||
fi
|
||||
base_command+=(posix "$LOCAL_FOLDER")
|
||||
export base_command
|
||||
|
||||
local versity_result
|
||||
start_versity_process "$3" || versity_result=$?
|
||||
if [[ $versity_result -ne 0 ]]; then
|
||||
echo "error starting versity process"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
run_versity_app_s3() {
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "run versity app w/s3 command requires process number"
|
||||
return 1
|
||||
fi
|
||||
base_command=("$VERSITY_EXE" --port=":7071" --access="$AWS_ACCESS_KEY_ID" --secret="$AWS_SECRET_ACCESS_KEY")
|
||||
if [ -n "$CERT" ] && [ -n "$KEY" ]; then
|
||||
base_command+=(--cert "$CERT" --key "$KEY")
|
||||
fi
|
||||
base_command+=(s3 --access="$AWS_ACCESS_KEY_ID_TWO" --secret="$AWS_SECRET_ACCESS_KEY_TWO" --region="$AWS_REGION" --endpoint=https://s3.amazonaws.com)
|
||||
export base_command
|
||||
|
||||
local versity_result
|
||||
start_versity_process "$1" || versity_result=$?
|
||||
if [[ $versity_result -ne 0 ]]; then
|
||||
echo "error starting versity process"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
run_versity_app() {
|
||||
if [[ $BACKEND == 'posix' ]]; then
|
||||
run_versity_app_posix "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" "1" || result_one=$?
|
||||
if [[ $result_one -ne 0 ]]; then
|
||||
echo "error starting versity app"
|
||||
return 1
|
||||
fi
|
||||
elif [[ $BACKEND == 's3' ]]; then
|
||||
run_versity_app_posix "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" "1" || result_one=$?
|
||||
if [[ $result_one -ne 0 ]]; then
|
||||
echo "error starting versity app"
|
||||
return 1
|
||||
fi
|
||||
run_versity_app_s3 "2" || result_two=$?
|
||||
if [[ $result_two -ne 0 ]]; then
|
||||
echo "error starting second versity app"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "unrecognized backend type $BACKEND"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop_single_process() {
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "stop single process function requires process ID"
|
||||
return 1
|
||||
fi
|
||||
if ps -p "$1" > /dev/null; then
|
||||
kill "$1"
|
||||
wait "$1" || true
|
||||
else
|
||||
echo "Process with PID $1 does not exist."
|
||||
fi
|
||||
}
|
||||
|
||||
stop_versity() {
|
||||
if [ "$RUN_VERSITYGW" == "false" ]; then
|
||||
return
|
||||
fi
|
||||
if [ -n "$versitygw_pid" ]; then
|
||||
if ps -p "$versitygw_pid" > /dev/null; then
|
||||
kill "$versitygw_pid"
|
||||
wait "$versitygw_pid" || true
|
||||
else
|
||||
echo "Process with PID $versitygw_pid does not exist."
|
||||
local result_one
|
||||
local result_two
|
||||
# shellcheck disable=SC2154
|
||||
stop_single_process "$versitygw_pid_1" || result_one=$?
|
||||
if [[ $result_one -ne 0 ]]; then
|
||||
echo "error stopping versity process"
|
||||
fi
|
||||
if [[ $BACKEND == 's3' ]]; then
|
||||
# shellcheck disable=SC2154
|
||||
stop_single_process "$versitygw_pid_2" || result_two=$?
|
||||
if [[ $result_two -ne 0 ]]; then
|
||||
echo "error stopping versity process two"
|
||||
fi
|
||||
else
|
||||
echo "versitygw_pid is not set or empty."
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user