Skip to content

Commit 7ad3820

Browse files
authored
Merge pull request #48 from Dewberry/error-discard-placeholder-obj
Discard 0 byte placeholder object when listing
2 parents 2726603 + b22b07f commit 7ad3820

File tree

7 files changed

+68
-10
lines changed

7 files changed

+68
-10
lines changed

auth/auth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func validateToken(tokenString string) (*Claims, error) {
123123
return claims, nil
124124
}
125125

126-
func overlap(s1 []string, s2 []string) bool {
126+
func Overlap(s1 []string, s2 []string) bool {
127127
for _, x := range s1 {
128128
for _, y := range s2 {
129129
if x == y {
@@ -164,7 +164,7 @@ func Authorize(handler echo.HandlerFunc, allowedRoles ...string) echo.HandlerFun
164164
// Store the claims in the echo.Context
165165
c.Set("claims", claims)
166166

167-
ok := overlap(claims.RealmAccess["roles"], allowedRoles)
167+
ok := Overlap(claims.RealmAccess["roles"], allowedRoles)
168168
if !ok {
169169
return c.JSON(http.StatusUnauthorized, "user is not authorized")
170170
}

auth/database.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Database interface {
1515
CheckUserPermission(userEmail, bucket, prefix string, operations []string) bool
1616
Close() error
1717
GetUserAccessiblePrefixes(userEmail, bucket string, operations []string) ([]string, error)
18+
AddBucketPermissions(userEmail, bucket string, prefixes []string, operation string) error
1819
}
1920

2021
type PostgresDB struct {
@@ -123,6 +124,26 @@ func (db *PostgresDB) CheckUserPermission(userEmail, bucket, prefix string, oper
123124
return hasPermission
124125
}
125126

127+
// AddBucketPermissions adds permissions for a user to access specific prefixes in a bucket for a given operation.
128+
func (db *PostgresDB) AddBucketPermissions(userEmail, bucket string, prefixes []string, operation string) error {
129+
allowedPrefixes := make([]string, len(prefixes))
130+
for i, prefix := range prefixes {
131+
allowedPrefixes[i] = fmt.Sprintf("/%s/%s", bucket, prefix)
132+
}
133+
134+
query := `
135+
INSERT INTO permissions (user_email, operation, allowed_s3_prefixes)
136+
VALUES ($1, $2, $3);
137+
`
138+
139+
_, err := db.Handle.Exec(query, userEmail, operation, pq.Array(allowedPrefixes))
140+
if err != nil {
141+
return fmt.Errorf("error adding bucket permissions: %v", err)
142+
}
143+
144+
return nil
145+
}
146+
126147
// Close closes the database connection.
127148
func (db *PostgresDB) Close() error {
128149
return db.Handle.Close()

blobstore/blobhandler.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,20 @@ func (bh *BlobHandler) HandleCheckS3UserPermission(c echo.Context) error {
335335
log.Error(errMsg.Error())
336336
return c.JSON(http.StatusForbidden, errMsg.Error())
337337
}
338-
prefix := c.QueryParam("prefix")
339-
bucket := c.QueryParam("bucket")
340-
operation := c.QueryParam("operation")
341338
claims, ok := c.Get("claims").(*auth.Claims)
342339
if !ok {
343340
errMsg := fmt.Errorf("could not get claims from request context")
344341
log.Error(errMsg.Error())
345342
return c.JSON(http.StatusInternalServerError, errMsg.Error())
346343
}
344+
isAdminOrWriter := auth.Overlap(claims.RealmAccess["roles"], []string{"s3_admin", "s3_writer"})
345+
if isAdminOrWriter {
346+
return c.JSON(http.StatusOK, true)
347+
}
348+
prefix := c.QueryParam("prefix")
349+
bucket := c.QueryParam("bucket")
350+
operation := c.QueryParam("operation")
351+
347352
userEmail := claims.Email
348353
if operation == "" || prefix == "" || bucket == "" {
349354
errMsg := fmt.Errorf("`prefix`, `operation` and 'bucket are required params")

blobstore/buckets.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ func (bh *BlobHandler) HandleListBuckets(c echo.Context) error {
9393
return c.JSON(http.StatusInternalServerError, fmt.Errorf("error fetching user permissions: %s", err.Error()))
9494
}
9595

96-
for _, controller := range bh.S3Controllers {
96+
for i := range bh.S3Controllers {
97+
controller := &bh.S3Controllers[i]
9798
if bh.AllowAllBuckets {
9899
result, err := controller.ListBuckets()
99100
if err != nil {

blobstore/delete.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ func (bh *BlobHandler) HandleDeleteObject(c echo.Context) error {
7373
return c.JSON(http.StatusUnprocessableEntity, errMsg.Error())
7474
}
7575

76+
httpCode, err := bh.CheckUserS3Permission(c, bucket, key, []string{"write"})
77+
if err != nil {
78+
errMsg := fmt.Errorf("error while checking for user permission: %s", err)
79+
log.Error(errMsg.Error())
80+
return c.JSON(httpCode, errMsg.Error())
81+
}
82+
7683
// If the key is not a folder, proceed with deleting a single object
7784
keyExist, err := s3Ctrl.KeyExists(bucket, key)
7885
if err != nil {
@@ -103,6 +110,7 @@ func (bh *BlobHandler) HandleDeleteObject(c echo.Context) error {
103110
}
104111

105112
func (bh *BlobHandler) HandleDeletePrefix(c echo.Context) error {
113+
106114
bucket := c.QueryParam("bucket")
107115
s3Ctrl, err := bh.GetController(bucket)
108116
if err != nil {
@@ -119,6 +127,14 @@ func (bh *BlobHandler) HandleDeletePrefix(c echo.Context) error {
119127
if !strings.HasSuffix(prefix, "/") {
120128
prefix = prefix + "/"
121129
}
130+
131+
httpCode, err := bh.CheckUserS3Permission(c, bucket, prefix, []string{"write"})
132+
if err != nil {
133+
errMsg := fmt.Errorf("error while checking for user permission: %s", err)
134+
log.Error(errMsg.Error())
135+
return c.JSON(httpCode, errMsg.Error())
136+
}
137+
122138
err = s3Ctrl.RecursivelyDeleteObjects(bucket, prefix)
123139
if err != nil {
124140
if strings.Contains(err.Error(), "prefix not found") {
@@ -190,7 +206,13 @@ func (bh *BlobHandler) HandleDeleteObjectsByList(c echo.Context) error {
190206
keys := make([]string, 0, len(deleteRequest.Keys))
191207
for _, p := range deleteRequest.Keys {
192208
s3Path := strings.TrimPrefix(p, "/")
193-
key := aws.String(s3Path)
209+
210+
httpCode, err := bh.CheckUserS3Permission(c, bucket, s3Path, []string{"write"})
211+
if err != nil {
212+
errMsg := fmt.Errorf("error while checking for user permission: %s", err)
213+
log.Error(errMsg.Error())
214+
return c.JSON(httpCode, errMsg.Error())
215+
}
194216

195217
// Check if the key exists before appending it to the keys list
196218
keyExists, err := s3Ctrl.KeyExists(bucket, s3Path)
@@ -205,6 +227,7 @@ func (bh *BlobHandler) HandleDeleteObjectsByList(c echo.Context) error {
205227
return c.JSON(http.StatusNotFound, errMsg.Error())
206228
}
207229

230+
key := aws.String(s3Path)
208231
keys = append(keys, *key)
209232
}
210233

blobstore/list.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ func (bh *BlobHandler) HandleListByPrefix(c echo.Context) error {
104104
}
105105
for _, object := range page.Contents {
106106
// Handle files
107+
// Skip zero-byte objects that match a common prefix with a trailing slash
108+
if *object.Size == 0 && strings.HasSuffix(*object.Key, "/") {
109+
continue
110+
}
107111
if fullAccess || IsPermittedPrefix(bucket, *object.Key, permissions) {
108112
result = append(result, aws.StringValue(object.Key))
109113
}
@@ -185,6 +189,10 @@ func (bh *BlobHandler) HandleListByPrefixWithDetail(c echo.Context) error {
185189

186190
for _, object := range page.Contents {
187191
// Handle files
192+
// Skip zero-byte objects that match a common prefix with a trailing slash
193+
if *object.Size == 0 && strings.HasSuffix(*object.Key, "/") {
194+
continue
195+
}
188196
if fullAccess || IsPermittedPrefix(bucket, *object.Key, permissions) {
189197
file := ListResult{
190198
ID: count,

main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func main() {
8888
e.PUT("/object/move", auth.Authorize(bh.HandleMoveObject, admin...))
8989
e.GET("/object/download", auth.Authorize(bh.HandleGetPresignedDownloadURL, allUsers...))
9090
e.POST("/object/upload", auth.Authorize(bh.HandleMultipartUpload, writers...)) //deprecated by presigned upload URL
91-
e.DELETE("/object/delete", auth.Authorize(bh.HandleDeleteObject, admin...))
91+
e.DELETE("/object/delete", auth.Authorize(bh.HandleDeleteObject, writers...))
9292
e.GET("/object/exists", auth.Authorize(bh.HandleGetObjExist, allUsers...))
9393
e.GET("/object/presigned_upload", auth.Authorize(bh.HandleGetPresignedUploadURL, writers...))
9494
e.GET("/object/multipart_upload_id", auth.Authorize(bh.HandleGetMultipartUploadID, writers...))
@@ -100,11 +100,11 @@ func main() {
100100
// e.GET("/prefix/download", auth.Authorize(bh.HandleGetPresignedURLMultiObj, allUsers...))
101101
e.GET("/prefix/download/script", auth.Authorize(bh.HandleGenerateDownloadScript, allUsers...))
102102
e.PUT("/prefix/move", auth.Authorize(bh.HandleMovePrefix, admin...))
103-
e.DELETE("/prefix/delete", auth.Authorize(bh.HandleDeletePrefix, admin...))
103+
e.DELETE("/prefix/delete", auth.Authorize(bh.HandleDeletePrefix, writers...))
104104
e.GET("/prefix/size", auth.Authorize(bh.HandleGetSize, allUsers...))
105105

106106
// universal
107-
e.DELETE("/delete_keys", auth.Authorize(bh.HandleDeleteObjectsByList, admin...))
107+
e.DELETE("/delete_keys", auth.Authorize(bh.HandleDeleteObjectsByList, writers...))
108108

109109
// multi-bucket
110110
e.GET("/list_buckets", auth.Authorize(bh.HandleListBuckets, allUsers...))

0 commit comments

Comments
 (0)