-
Notifications
You must be signed in to change notification settings - Fork 1
/
s3_agent.go
150 lines (136 loc) · 3.81 KB
/
s3_agent.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package s3_agent
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/snapp-incubator/ceph-s3-operator/pkg/consts"
)
// S3Agent wraps the s3.S3 structure to allow for wrapper methods
type S3Agent struct {
Client *s3.S3
}
func NewS3Agent(accessKey, secretKey, endpoint string, debug bool) (*S3Agent, error) {
// TODO: cephRegion must not be hardcoded.
const cephRegion = "us-east-1"
logLevel := aws.LogOff
if debug {
logLevel = aws.LogDebug
}
client := http.Client{
Timeout: time.Second * 15,
}
sess, err := session.NewSession(
aws.NewConfig().
WithRegion(cephRegion).
WithCredentials(credentials.NewStaticCredentials(accessKey, secretKey, "")).
WithEndpoint(endpoint).
WithS3ForcePathStyle(true).
WithMaxRetries(5).
WithDisableSSL(true).
WithHTTPClient(&client).
WithLogLevel(logLevel),
)
if err != nil {
return nil, err
}
svc := s3.New(sess)
return &S3Agent{
Client: svc,
}, nil
}
func (s *S3Agent) CreateBucket(name string) error {
bucketInput := &s3.CreateBucketInput{
Bucket: &name,
}
_, err := s.Client.CreateBucket(bucketInput)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case s3.ErrCodeBucketAlreadyExists:
return nil
case s3.ErrCodeBucketAlreadyOwnedByYou:
return nil
}
}
return fmt.Errorf("failed to create bucket %q. %w", name, err)
}
return nil
}
func (s *S3Agent) DeleteBucket(name string) error {
bucketInput := &s3.DeleteBucketInput{
Bucket: &name,
}
_, err := s.Client.DeleteBucket(bucketInput)
return err
}
func (s *S3Agent) SetBucketPolicy(subuserAccessMap map[string]string, tenant string,
owner string, bucket string) (string, error) {
// The map of access levels to the AWS IAM names slice
accessAWSIAMMap := make(map[string][]string)
policy := map[string]interface{}{
"Version": "2012-10-17",
"Id": "S3Policy",
}
statementSlice := []map[string]interface{}{}
for subuser, access := range subuserAccessMap {
// Create AWS IAM Name needed for the policy from the subuser name
aws_iam := fmt.Sprintf("arn:aws:iam::%s:user/%s:%s", tenant, owner, subuser)
accessAWSIAMMap[access] = append(accessAWSIAMMap[access], aws_iam)
}
// Iterate over different levels
for access, AWS_iam := range accessAWSIAMMap {
principal := map[string][]string{}
statement := map[string]interface{}{
"Sid": "BucketAllow",
"Effect": "Allow",
"Principal": map[string][]string{},
"Action": []string{},
"Resource": []string{
fmt.Sprintf("arn:aws:s3::%s:%s", tenant, bucket),
fmt.Sprintf("arn:aws:s3::%s:%s/*", tenant, bucket),
},
}
principal["AWS"] = AWS_iam
statement["Principal"] = principal
bucketAccessAction := generateBucketAccessAction()
if actions, exists := bucketAccessAction[access]; exists {
statement["Action"] = actions
} else {
return "", fmt.Errorf("the access %s doesn't exists", access)
}
// Append the statement
statementSlice = append(statementSlice, statement)
}
policy["Statement"] = statementSlice
policyMarshal, err := json.Marshal(policy)
policyInput := s3.PutBucketPolicyInput{Bucket: aws.String(bucket),
Policy: aws.String(string(policyMarshal))}
if err != nil {
return "", err
}
_, err = s.Client.PutBucketPolicy(&policyInput)
if err != nil {
return "", err
}
return string(policyMarshal), nil
}
func generateBucketAccessAction() map[string][]string {
readActions := []string{
"s3:ListBucket",
"s3:GetObject",
}
writeActions := []string{
"s3:DeleteObject",
"s3:PutObject",
}
return map[string][]string{
consts.BucketAccessRead: readActions,
consts.BucketAccessWrite: append(readActions, writeActions...),
}
}