Merge pull request #270 from versity/ben/iam_refactor

This commit is contained in:
Ben McClelland
2023-10-02 12:30:01 -07:00
committed by GitHub
16 changed files with 384 additions and 364 deletions

View File

@@ -17,7 +17,6 @@ package auth
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/aws/aws-sdk-go-v2/service/s3"
@@ -236,29 +235,14 @@ func VerifyACL(acl ACL, access string, permission types.Permission, isRoot bool)
return s3err.GetAPIError(s3err.ErrAccessDenied)
}
func IsAdmin(access string, isRoot bool) error {
var data IAMConfig
func IsAdmin(acct Account, isRoot bool) error {
if isRoot {
return nil
}
file, err := os.ReadFile("users.json")
if err != nil {
return fmt.Errorf("unable to read config file: %w", err)
}
if err := json.Unmarshal(file, &data); err != nil {
return err
}
acc, ok := data.AccessAccounts[access]
if !ok {
return fmt.Errorf("user does not exist")
}
if acc.Role == "admin" {
if acct.Role == "admin" {
return nil
}
return s3err.GetAPIError(s3err.ErrAccessDenied)
}

View File

@@ -29,7 +29,7 @@ type Account struct {
//
//go:generate moq -out ../s3api/controllers/iam_moq_test.go -pkg controllers . IAMService
type IAMService interface {
CreateAccount(access string, account Account) error
CreateAccount(account Account) error
GetUserAccount(access string) (Account, error)
DeleteUserAccount(access string) error
ListUserAccounts() ([]Account, error)

View File

@@ -16,45 +16,59 @@ package auth
import (
"encoding/json"
"errors"
"fmt"
"hash/crc32"
"io/fs"
"os"
"path/filepath"
"sync"
"time"
)
const (
iamFile = "users.json"
iamBackupFile = "users.json.backup"
)
var (
cacheDuration = 5 * time.Minute
)
// IAMServiceInternal manages the internal IAM service
type IAMServiceInternal struct {
storer Storer
path string
mu sync.RWMutex
accts IAMConfig
serial uint32
mu sync.RWMutex
accts iAMConfig
serial uint32
iamcache []byte
iamvalid bool
iamexpire time.Time
}
// UpdateAcctFunc accepts the current data and returns the new data to be stored
type UpdateAcctFunc func([]byte) ([]byte, error)
// Storer is the interface to manage the peristent IAM data for the internal
// IAM service
type Storer interface {
InitIAM() error
GetIAM() ([]byte, error)
StoreIAM(UpdateAcctFunc) error
}
// IAMConfig stores all internal IAM accounts
type IAMConfig struct {
// iAMConfig stores all internal IAM accounts
type iAMConfig struct {
AccessAccounts map[string]Account `json:"accessAccounts"`
}
var _ IAMService = &IAMServiceInternal{}
// NewInternal creates a new instance for the Internal IAM service
func NewInternal(s Storer) (*IAMServiceInternal, error) {
func NewInternal(path string) (*IAMServiceInternal, error) {
i := &IAMServiceInternal{
storer: s,
path: path,
}
err := i.updateCache()
err := i.initIAM()
if err != nil {
return nil, fmt.Errorf("init iam: %w", err)
}
err = i.updateCache()
if err != nil {
return nil, fmt.Errorf("refresh iam cache: %w", err)
}
@@ -64,26 +78,26 @@ func NewInternal(s Storer) (*IAMServiceInternal, error) {
// CreateAccount creates a new IAM account. Returns an error if the account
// already exists.
func (s *IAMServiceInternal) CreateAccount(access string, account Account) error {
func (s *IAMServiceInternal) CreateAccount(account Account) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.storer.StoreIAM(func(data []byte) ([]byte, error) {
var conf IAMConfig
return s.storeIAM(func(data []byte) ([]byte, error) {
var conf iAMConfig
if len(data) > 0 {
if err := json.Unmarshal(data, &conf); err != nil {
return nil, fmt.Errorf("failed to parse iam: %w", err)
}
} else {
conf = IAMConfig{AccessAccounts: map[string]Account{}}
conf = iAMConfig{AccessAccounts: map[string]Account{}}
}
_, ok := conf.AccessAccounts[access]
_, ok := conf.AccessAccounts[account.Access]
if ok {
return nil, fmt.Errorf("account already exists")
}
conf.AccessAccounts[access] = account
conf.AccessAccounts[account.Access] = account
b, err := json.Marshal(conf)
if err != nil {
@@ -101,7 +115,7 @@ func (s *IAMServiceInternal) GetUserAccount(access string) (Account, error) {
s.mu.RLock()
defer s.mu.RUnlock()
data, err := s.storer.GetIAM()
data, err := s.getIAM()
if err != nil {
return Account{}, fmt.Errorf("get iam data: %w", err)
}
@@ -129,7 +143,7 @@ func (s *IAMServiceInternal) updateCache() error {
s.mu.Lock()
defer s.mu.Unlock()
data, err := s.storer.GetIAM()
data, err := s.getIAM()
if err != nil {
return fmt.Errorf("get iam data: %w", err)
}
@@ -155,13 +169,13 @@ func (s *IAMServiceInternal) DeleteUserAccount(access string) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.storer.StoreIAM(func(data []byte) ([]byte, error) {
return s.storeIAM(func(data []byte) ([]byte, error) {
if len(data) == 0 {
// empty config, do nothing
return data, nil
}
var conf IAMConfig
var conf iAMConfig
if err := json.Unmarshal(data, &conf); err != nil {
return nil, fmt.Errorf("failed to parse iam: %w", err)
@@ -185,7 +199,7 @@ func (s *IAMServiceInternal) ListUserAccounts() (accs []Account, err error) {
s.mu.RLock()
defer s.mu.RUnlock()
data, err := s.storer.GetIAM()
data, err := s.getIAM()
if err != nil {
return []Account{}, fmt.Errorf("get iam data: %w", err)
}
@@ -210,3 +224,186 @@ func (s *IAMServiceInternal) ListUserAccounts() (accs []Account, err error) {
return accs, nil
}
const (
iamMode = 0600
)
func (s *IAMServiceInternal) initIAM() error {
fname := filepath.Join(s.path, iamFile)
_, err := os.ReadFile(fname)
if errors.Is(err, fs.ErrNotExist) {
b, err := json.Marshal(iAMConfig{AccessAccounts: map[string]Account{}})
if err != nil {
return fmt.Errorf("marshal default iam: %w", err)
}
err = os.WriteFile(fname, b, iamMode)
if err != nil {
return fmt.Errorf("write default iam: %w", err)
}
}
return nil
}
func (s *IAMServiceInternal) getIAM() ([]byte, error) {
if !s.iamvalid || !s.iamexpire.After(time.Now()) {
err := s.refreshIAM()
if err != nil {
return nil, err
}
}
return s.iamcache, nil
}
const (
backoff = 100 * time.Millisecond
maxretry = 300
)
func (s *IAMServiceInternal) refreshIAM() error {
// We are going to be racing with other running gateways without any
// coordination. So we might find the file does not exist at times.
// For this case we need to retry for a while assuming the other gateway
// will eventually write the file. If it doesn't after the max retries,
// then we will return the error.
retries := 0
for {
b, err := os.ReadFile(filepath.Join(s.path, iamFile))
if errors.Is(err, fs.ErrNotExist) {
// racing with someone else updating
// keep retrying after backoff
retries++
if retries < maxretry {
time.Sleep(backoff)
continue
}
return fmt.Errorf("read iam file: %w", err)
}
if err != nil {
return err
}
s.iamcache = b
s.iamvalid = true
s.iamexpire = time.Now().Add(cacheDuration)
break
}
return nil
}
func (s *IAMServiceInternal) storeIAM(update UpdateAcctFunc) error {
// We are going to be racing with other running gateways without any
// coordination. So the strategy here is to read the current file data.
// If the file doesn't exist, then we assume someone else is currently
// updating the file. So we just need to keep retrying. We also need
// to make sure the data is consistent within a single update. So racing
// writes to a file would possibly leave this in some invalid state.
// We can get atomic updates with rename. If we read the data, update
// the data, write to a temp file, then rename the tempfile back to the
// data file. This should always result in a complete data image.
// There is at least one unsolved failure mode here.
// If a gateway removes the data file and then crashes, all other
// gateways will retry forever thinking that the original will eventually
// write the file.
retries := 0
fname := filepath.Join(s.path, iamFile)
for {
b, err := os.ReadFile(fname)
if errors.Is(err, fs.ErrNotExist) {
// racing with someone else updating
// keep retrying after backoff
retries++
if retries < maxretry {
time.Sleep(backoff)
continue
}
// we have been unsuccessful trying to read the iam file
// so this must be the case where something happened and
// the file did not get updated successfully, and probably
// isn't going to be. The recovery procedure would be to
// copy the backup file into place of the original.
return fmt.Errorf("no iam file, needs backup recovery")
}
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("read iam file: %w", err)
}
// reset retries on successful read
retries = 0
err = os.Remove(iamFile)
if errors.Is(err, fs.ErrNotExist) {
// racing with someone else updating
// keep retrying after backoff
time.Sleep(backoff)
continue
}
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("remove old iam file: %w", err)
}
// save copy of data
datacopy := make([]byte, len(b))
copy(datacopy, b)
// make a backup copy in case we crash before update
// this is after remove, so there is a small window something
// can go wrong, but the remove should barrier other gateways
// from trying to write backup at the same time. Only one
// gateway will successfully remove the file.
os.WriteFile(filepath.Join(s.path, iamBackupFile), b, iamMode)
b, err = update(b)
if err != nil {
// update failed, try to write old data back out
os.WriteFile(fname, datacopy, iamMode)
return fmt.Errorf("update iam data: %w", err)
}
err = s.writeTempFile(b)
if err != nil {
// update failed, try to write old data back out
os.WriteFile(fname, datacopy, iamMode)
return err
}
s.iamcache = b
s.iamvalid = true
s.iamexpire = time.Now().Add(cacheDuration)
break
}
return nil
}
func (s *IAMServiceInternal) writeTempFile(b []byte) error {
fname := filepath.Join(s.path, iamFile)
f, err := os.CreateTemp(s.path, iamFile)
if err != nil {
return fmt.Errorf("create temp file: %w", err)
}
defer os.Remove(f.Name())
_, err = f.Write(b)
if err != nil {
return fmt.Errorf("write temp file: %w", err)
}
err = os.Rename(f.Name(), fname)
if err != nil {
return fmt.Errorf("rename temp file: %w", err)
}
return nil
}

40
auth/iam_single.go Normal file
View File

@@ -0,0 +1,40 @@
// 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"
// IAMServiceSingle manages the single tenant (root-only) IAM service
type IAMServiceSingle struct{}
// CreateAccount not valid in single tenant mode
func (IAMServiceSingle) CreateAccount(account Account) error {
return fmt.Errorf("create user not valid in single tenant mode")
}
// GetUserAccount no accounts in single tenant mode
func (IAMServiceSingle) GetUserAccount(access string) (Account, error) {
return Account{}, ErrNoSuchUser
}
// DeleteUserAccount no accounts in single tenant mode
func (IAMServiceSingle) DeleteUserAccount(access string) error {
return ErrNoSuchUser
}
// ListUserAccounts no accounts in single tenant mode
func (IAMServiceSingle) ListUserAccounts() ([]Account, error) {
return []Account{}, nil
}

View File

@@ -29,9 +29,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
@@ -48,19 +46,10 @@ type Posix struct {
rootfd *os.File
rootdir string
mu sync.RWMutex
iamcache []byte
iamvalid bool
iamexpire time.Time
}
var _ backend.Backend = &Posix{}
var (
cacheDuration = 5 * time.Minute
)
const (
metaTmpDir = ".sgwtmp"
metaTmpMultipartDir = metaTmpDir + "/multipart"
@@ -70,8 +59,6 @@ const (
contentTypeHdr = "content-type"
contentEncHdr = "content-encoding"
emptyMD5 = "d41d8cd98f00b204e9800998ecf8427e"
iamFile = "users.json"
iamBackupFile = "users.json.backup"
aclkey = "user.acl"
etagkey = "user.etag"
)
@@ -148,6 +135,8 @@ func (p *Posix) ListBuckets(_ context.Context, owner string, isAdmin bool) (s3re
sort.Sort(backend.ByBucketName(buckets))
fmt.Println("ListAllMyBucketsResult owner:", owner)
return s3response.ListAllMyBucketsResult{
Buckets: s3response.ListAllMyBucketsList{
Bucket: buckets,
@@ -1654,198 +1643,6 @@ func (p *Posix) ListBucketsAndOwners(ctx context.Context) (buckets []s3response.
return buckets, nil
}
const (
iamMode = 0600
)
func (p *Posix) InitIAM() error {
p.mu.RLock()
defer p.mu.RUnlock()
_, err := os.ReadFile(iamFile)
if errors.Is(err, fs.ErrNotExist) {
b, err := json.Marshal(auth.IAMConfig{AccessAccounts: map[string]auth.Account{}})
if err != nil {
return fmt.Errorf("marshal default iam: %w", err)
}
err = os.WriteFile(iamFile, b, iamMode)
if err != nil {
return fmt.Errorf("write default iam: %w", err)
}
}
return nil
}
func (p *Posix) GetIAM() ([]byte, error) {
p.mu.RLock()
defer p.mu.RUnlock()
if !p.iamvalid || !p.iamexpire.After(time.Now()) {
p.mu.RUnlock()
err := p.refreshIAM()
p.mu.RLock()
if err != nil {
return nil, err
}
}
return p.iamcache, nil
}
const (
backoff = 100 * time.Millisecond
maxretry = 300
)
func (p *Posix) refreshIAM() error {
p.mu.Lock()
defer p.mu.Unlock()
// We are going to be racing with other running gateways without any
// coordination. So we might find the file does not exist at times.
// For this case we need to retry for a while assuming the other gateway
// will eventually write the file. If it doesn't after the max retries,
// then we will return the error.
retries := 0
for {
b, err := os.ReadFile(iamFile)
if errors.Is(err, fs.ErrNotExist) {
// racing with someone else updating
// keep retrying after backoff
retries++
if retries < maxretry {
time.Sleep(backoff)
continue
}
return fmt.Errorf("read iam file: %w", err)
}
if err != nil {
return err
}
p.iamcache = b
p.iamvalid = true
p.iamexpire = time.Now().Add(cacheDuration)
break
}
return nil
}
func (p *Posix) StoreIAM(update auth.UpdateAcctFunc) error {
p.mu.Lock()
defer p.mu.Unlock()
// We are going to be racing with other running gateways without any
// coordination. So the strategy here is to read the current file data.
// If the file doesn't exist, then we assume someone else is currently
// updating the file. So we just need to keep retrying. We also need
// to make sure the data is consistent within a single update. So racing
// writes to a file would possibly leave this in some invalid state.
// We can get atomic updates with rename. If we read the data, update
// the data, write to a temp file, then rename the tempfile back to the
// data file. This should always result in a complete data image.
// There is at least one unsolved failure mode here.
// If a gateway removes the data file and then crashes, all other
// gateways will retry forever thinking that the original will eventually
// write the file.
retries := 0
for {
b, err := os.ReadFile(iamFile)
if errors.Is(err, fs.ErrNotExist) {
// racing with someone else updating
// keep retrying after backoff
retries++
if retries < maxretry {
time.Sleep(backoff)
continue
}
// we have been unsuccessful trying to read the iam file
// so this must be the case where something happened and
// the file did not get updated successfully, and probably
// isn't going to be. The recovery procedure would be to
// copy the backup file into place of the original.
return fmt.Errorf("no iam file, needs backup recovery")
}
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("read iam file: %w", err)
}
// reset retries on successful read
retries = 0
err = os.Remove(iamFile)
if errors.Is(err, fs.ErrNotExist) {
// racing with someone else updating
// keep retrying after backoff
time.Sleep(backoff)
continue
}
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("remove old iam file: %w", err)
}
// save copy of data
datacopy := make([]byte, len(b))
copy(datacopy, b)
// make a backup copy in case we crash before update
// this is after remove, so there is a small window something
// can go wrong, but the remove should barrier other gateways
// from trying to write backup at the same time. Only one
// gateway will successfully remove the file.
os.WriteFile(iamBackupFile, b, iamMode)
b, err = update(b)
if err != nil {
// update failed, try to write old data back out
os.WriteFile(iamFile, datacopy, iamMode)
return fmt.Errorf("update iam data: %w", err)
}
err = writeTempFile(b)
if err != nil {
// update failed, try to write old data back out
os.WriteFile(iamFile, datacopy, iamMode)
return err
}
p.iamcache = b
p.iamvalid = true
p.iamexpire = time.Now().Add(cacheDuration)
break
}
return nil
}
func writeTempFile(b []byte) error {
f, err := os.CreateTemp(".", iamFile)
if err != nil {
return fmt.Errorf("create temp file: %w", err)
}
defer os.Remove(f.Name())
_, err = f.Write(b)
if err != nil {
return fmt.Errorf("write temp file: %w", err)
}
err = os.Rename(f.Name(), iamFile)
if err != nil {
return fmt.Errorf("rename temp file: %w", err)
}
return nil
}
func isNoAttr(err error) bool {
if err == nil {
return false

View File

@@ -44,6 +44,7 @@ var (
logWebhookURL string
accessLog string
debug bool
iamDir string
)
var (
@@ -207,10 +208,15 @@ func initFlags() []cli.Flag {
Destination: &natsTopic,
Aliases: []string{"ent"},
},
&cli.StringFlag{
Name: "iam-dir",
Usage: "if defined, run internal iam service within this directory",
Destination: &iamDir,
},
}
}
func runGateway(ctx *cli.Context, be backend.Backend, s auth.Storer) error {
func runGateway(ctx *cli.Context, be backend.Backend) error {
// int32 max for 32 bit arch
blimit := int64(2*1024*1024*1024 - 1)
if strconv.IntSize > 32 {
@@ -269,14 +275,18 @@ func runGateway(ctx *cli.Context, be backend.Backend, s auth.Storer) error {
admOpts = append(admOpts, s3api.WithAdminSrvTLS(cert))
}
err := s.InitIAM()
if err != nil {
return fmt.Errorf("init iam: %w", err)
}
iam, err := auth.NewInternal(s)
if err != nil {
return fmt.Errorf("setup internal iam service: %w", err)
var iam auth.IAMService
switch {
case iamDir != "":
var err error
iam, err = auth.NewInternal(iamDir)
if err != nil {
return fmt.Errorf("setup internal iam service: %w", err)
}
default:
// default gateway to single user mode when
// no other iam service configured
iam = auth.IAMServiceSingle{}
}
logger, err := s3log.InitLogger(&s3log.LogConfig{

View File

@@ -49,5 +49,5 @@ func runPosix(ctx *cli.Context) error {
return fmt.Errorf("init posix: %v", err)
}
return runGateway(ctx, be, be)
return runGateway(ctx, be)
}

View File

@@ -69,5 +69,5 @@ func runScoutfs(ctx *cli.Context) error {
return fmt.Errorf("init scoutfs: %v", err)
}
return runGateway(ctx, be, be)
return runGateway(ctx, be)
}

View File

@@ -6,7 +6,7 @@ rm -rf /tmp/covdata
mkdir /tmp/covdata
# run server in background
GOCOVERDIR=/tmp/covdata ./versitygw -a user -s pass posix /tmp/gw &
GOCOVERDIR=/tmp/covdata ./versitygw -a user -s pass --iam-dir /tmp/gw posix /tmp/gw &
GW_PID=$!
# wait a second for server to start up

View File

@@ -33,18 +33,18 @@ func NewAdminController(iam auth.IAMService, be backend.Backend) AdminController
func (c AdminController) CreateUser(ctx *fiber.Ctx) error {
access, secret, role := ctx.Query("access"), ctx.Query("secret"), ctx.Query("role")
requesterRole := ctx.Locals("role").(string)
acct := ctx.Locals("account").(auth.Account)
if requesterRole != "admin" {
if acct.Role != "admin" {
return fmt.Errorf("access denied: only admin users have access to this resource")
}
if role != "user" && role != "admin" {
return fmt.Errorf("invalid parameters: user role have to be one of the following: 'user', 'admin'")
}
user := auth.Account{Secret: secret, Role: role}
user := auth.Account{Access: access, Secret: secret, Role: role}
err := c.iam.CreateAccount(access, user)
err := c.iam.CreateAccount(user)
if err != nil {
return fmt.Errorf("failed to create a user: %w", err)
}
@@ -54,8 +54,8 @@ func (c AdminController) CreateUser(ctx *fiber.Ctx) error {
func (c AdminController) DeleteUser(ctx *fiber.Ctx) error {
access := ctx.Query("access")
requesterRole := ctx.Locals("role").(string)
if requesterRole != "admin" {
acct := ctx.Locals("account").(auth.Account)
if acct.Role != "admin" {
return fmt.Errorf("access denied: only admin users have access to this resource")
}
@@ -68,8 +68,8 @@ func (c AdminController) DeleteUser(ctx *fiber.Ctx) error {
}
func (c AdminController) ListUsers(ctx *fiber.Ctx) error {
role := ctx.Locals("role").(string)
if role != "admin" {
acct := ctx.Locals("account").(auth.Account)
if acct.Role != "admin" {
return fmt.Errorf("access denied: only admin users have access to this resource")
}
accs, err := c.iam.ListUserAccounts()
@@ -81,8 +81,8 @@ func (c AdminController) ListUsers(ctx *fiber.Ctx) error {
}
func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) error {
role := ctx.Locals("role").(string)
if role != "admin" {
acct := ctx.Locals("account").(auth.Account)
if acct.Role != "admin" {
return fmt.Errorf("access denied: only admin users have access to this resource")
}
owner := ctx.Query("owner")
@@ -105,8 +105,8 @@ func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) error {
}
func (c AdminController) ListBuckets(ctx *fiber.Ctx) error {
role := ctx.Locals("role").(string)
if role != "admin" {
acct := ctx.Locals("account").(auth.Account)
if acct.Role != "admin" {
return fmt.Errorf("access denied: only admin users have access to this resource")
}

View File

@@ -33,7 +33,7 @@ func TestAdminController_CreateUser(t *testing.T) {
adminController := AdminController{
iam: &IAMServiceMock{
CreateAccountFunc: func(access string, account auth.Account) error {
CreateAccountFunc: func(account auth.Account) error {
return nil
},
},
@@ -42,7 +42,7 @@ func TestAdminController_CreateUser(t *testing.T) {
app := fiber.New()
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -51,7 +51,7 @@ func TestAdminController_CreateUser(t *testing.T) {
appErr := fiber.New()
appErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "user")
ctx.Locals("account", auth.Account{Access: "user1", Secret: "secret", Role: "user"})
return ctx.Next()
})
@@ -121,7 +121,7 @@ func TestAdminController_DeleteUser(t *testing.T) {
app := fiber.New()
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -130,7 +130,7 @@ func TestAdminController_DeleteUser(t *testing.T) {
appErr := fiber.New()
appErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "user")
ctx.Locals("account", auth.Account{Access: "user1", Secret: "secret", Role: "user"})
return ctx.Next()
})
@@ -199,7 +199,7 @@ func TestAdminController_ListUsers(t *testing.T) {
appErr := fiber.New()
appErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -208,7 +208,7 @@ func TestAdminController_ListUsers(t *testing.T) {
appRoleErr := fiber.New()
appRoleErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "user")
ctx.Locals("account", auth.Account{Access: "user1", Secret: "secret", Role: "user"})
return ctx.Next()
})
@@ -217,7 +217,7 @@ func TestAdminController_ListUsers(t *testing.T) {
appSucc := fiber.New()
appSucc.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -307,7 +307,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
app := fiber.New()
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -316,7 +316,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
appRoleErr := fiber.New()
appRoleErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "user")
ctx.Locals("account", auth.Account{Access: "user1", Secret: "secret", Role: "user"})
return ctx.Next()
})
@@ -325,7 +325,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
appIamErr := fiber.New()
appIamErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -334,7 +334,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
appIamNoSuchUser := fiber.New()
appIamNoSuchUser.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -412,7 +412,7 @@ func TestAdminController_ListBuckets(t *testing.T) {
app := fiber.New()
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "admin1", Secret: "secret", Role: "admin"})
return ctx.Next()
})
@@ -421,7 +421,7 @@ func TestAdminController_ListBuckets(t *testing.T) {
appRoleErr := fiber.New()
appRoleErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("role", "user")
ctx.Locals("account", auth.Account{Access: "user1", Secret: "secret", Role: "user"})
return ctx.Next()
})

View File

@@ -53,8 +53,8 @@ func New(be backend.Backend, iam auth.IAMService, logger s3log.AuditLogger, evs
}
func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) error {
access, role := ctx.Locals("access").(string), ctx.Locals("role").(string)
res, err := c.be.ListBuckets(ctx.Context(), access, role == "admin")
acct := ctx.Locals("account").(auth.Account)
res, err := c.be.ListBuckets(ctx.Context(), acct.Access, acct.Role == "admin")
return SendXMLResponse(ctx, res, err, &MetaOpts{Logger: c.logger, Action: "ListBucket"})
}
@@ -66,7 +66,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
maxParts := ctx.QueryInt("max-parts", 0)
partNumberMarker := ctx.Query("part-number-marker")
acceptRange := ctx.Get("Range")
access := ctx.Locals("access").(string)
acct := ctx.Locals("account").(auth.Account)
isRoot := ctx.Locals("isRoot").(bool)
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
if keyEnd != "" {
@@ -74,7 +74,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
}
if ctx.Request().URI().QueryArgs().Has("tagging") {
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "GetObjectTagging", BucketOwner: parsedAcl.Owner})
}
@@ -102,7 +102,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
}
}
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "ListParts", BucketOwner: parsedAcl.Owner})
}
@@ -117,7 +117,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
}
if ctx.Request().URI().QueryArgs().Has("acl") {
if err := auth.VerifyACL(parsedAcl, access, "READ_ACP", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ_ACP", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "GetObjectAcl", BucketOwner: parsedAcl.Owner})
}
res, err := c.be.GetObjectAcl(ctx.Context(), &s3.GetObjectAclInput{
@@ -128,7 +128,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
}
if attrs := ctx.Get("X-Amz-Object-Attributes"); attrs != "" {
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "GetObjectAttributes", BucketOwner: parsedAcl.Owner})
}
var oattrs []types.ObjectAttributes
@@ -143,7 +143,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
return SendXMLResponse(ctx, res, err, &MetaOpts{Logger: c.logger, Action: "GetObjectAttributes", BucketOwner: parsedAcl.Owner})
}
if err := auth.VerifyACL(parsedAcl, access, "READ_ACP", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ_ACP", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "GetObject", BucketOwner: parsedAcl.Owner})
}
@@ -224,12 +224,12 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
keyMarker := ctx.Query("key-marker")
maxUploadsStr := ctx.Query("max-uploads")
uploadIdMarker := ctx.Query("upload-id-marker")
access := ctx.Locals("access").(string)
acct := ctx.Locals("account").(auth.Account)
isRoot := ctx.Locals("isRoot").(bool)
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
if ctx.Request().URI().QueryArgs().Has("acl") {
if err := auth.VerifyACL(parsedAcl, access, "READ_ACP", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ_ACP", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "GetBucketAcl", BucketOwner: parsedAcl.Owner})
}
@@ -243,7 +243,7 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
}
if ctx.Request().URI().QueryArgs().Has("uploads") {
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "ListMultipartUploads", BucketOwner: parsedAcl.Owner})
}
maxUploads, err := utils.ParseUint(maxUploadsStr)
@@ -266,7 +266,7 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
}
if ctx.QueryInt("list-type") == 2 {
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "ListObjectsV2", BucketOwner: parsedAcl.Owner})
}
maxkeys, err := utils.ParseUint(maxkeysStr)
@@ -287,7 +287,7 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
return SendXMLResponse(ctx, res, err, &MetaOpts{Logger: c.logger, Action: "ListObjectsV2", BucketOwner: parsedAcl.Owner})
}
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "ListObjects", BucketOwner: parsedAcl.Owner})
}
@@ -311,7 +311,7 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
}
func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
bucket, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP, access, isRoot :=
bucket, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP, acct, isRoot :=
ctx.Params("bucket"),
ctx.Get("X-Amz-Acl"),
ctx.Get("X-Amz-Grant-Full-Control"),
@@ -319,7 +319,7 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
ctx.Get("X-Amz-Grant-Read-Acp"),
ctx.Get("X-Amz-Grant-Write"),
ctx.Get("X-Amz-Grant-Write-Acp"),
ctx.Locals("access").(string),
ctx.Locals("account").(auth.Account),
ctx.Locals("isRoot").(bool)
grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP
@@ -329,7 +329,7 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
var accessControlPolicy auth.AccessControlPolicy
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
if err := auth.VerifyACL(parsedAcl, access, "WRITE_ACP", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE_ACP", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "PutBucketAcl", BucketOwner: parsedAcl.Owner})
}
@@ -391,9 +391,9 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
err := c.be.CreateBucket(ctx.Context(), &s3.CreateBucketInput{
Bucket: &bucket,
ObjectOwnership: types.ObjectOwnership(access),
ObjectOwnership: types.ObjectOwnership(acct.Access),
})
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "CreateBucket", BucketOwner: access})
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "CreateBucket", BucketOwner: acct.Access})
}
func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
@@ -401,7 +401,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
keyStart := ctx.Params("key")
keyEnd := ctx.Params("*1")
uploadId := ctx.Query("uploadId")
access := ctx.Locals("access").(string)
acct := ctx.Locals("account").(auth.Account)
isRoot := ctx.Locals("isRoot").(bool)
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
tagging := ctx.Get("x-amz-tagging")
@@ -452,7 +452,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
tags[tag.Key] = tag.Value
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "PutObjectTagging", BucketOwner: parsedAcl.Owner})
}
@@ -490,7 +490,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidPart), &MetaOpts{Logger: c.logger, Action: "UploadPart", BucketOwner: parsedAcl.Owner})
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "UploadPart", BucketOwner: parsedAcl.Owner})
}
@@ -574,7 +574,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
}
if copySource != "" {
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "CopyObject", BucketOwner: parsedAcl.Owner})
}
@@ -604,7 +604,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
CopySourceIfNoneMatch: &copySrcIfNoneMatch,
CopySourceIfModifiedSince: &mtime,
CopySourceIfUnmodifiedSince: &umtime,
ExpectedBucketOwner: &access,
ExpectedBucketOwner: &acct.Access,
Metadata: metadata,
})
if err == nil {
@@ -628,7 +628,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
metadata := utils.GetUserMetaData(&ctx.Request().Header)
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "PutObject", BucketOwner: parsedAcl.Owner})
}
@@ -659,9 +659,9 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
}
func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error {
bucket, access, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
bucket, acct, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("account").(auth.Account), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "DeleteBucket", BucketOwner: parsedAcl.Owner})
}
@@ -672,14 +672,14 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error {
}
func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error {
bucket, access, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
bucket, acct, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("account").(auth.Account), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
var dObj s3response.DeleteObjects
if err := xml.Unmarshal(ctx.Body(), &dObj); err != nil {
return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidRequest), &MetaOpts{Logger: c.logger, Action: "DeleteObjects", BucketOwner: parsedAcl.Owner})
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "DeleteObjects", BucketOwner: parsedAcl.Owner})
}
@@ -697,7 +697,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
key := ctx.Params("key")
keyEnd := ctx.Params("*1")
uploadId := ctx.Query("uploadId")
access := ctx.Locals("access").(string)
acct := ctx.Locals("account").(auth.Account)
isRoot := ctx.Locals("isRoot").(bool)
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
@@ -706,7 +706,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
}
if ctx.Request().URI().QueryArgs().Has("tagging") {
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "RemoveObjectTagging", BucketOwner: parsedAcl.Owner})
}
@@ -724,7 +724,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
if uploadId != "" {
expectedBucketOwner, requestPayer := ctx.Get("X-Amz-Expected-Bucket-Owner"), ctx.Get("X-Amz-Request-Payer")
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "AbortMultipartUpload", BucketOwner: parsedAcl.Owner})
}
@@ -738,7 +738,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "AbortMultipartUpload", BucketOwner: parsedAcl.Owner, Status: 204})
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "DeleteObject", BucketOwner: parsedAcl.Owner})
}
@@ -757,9 +757,9 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
}
func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) error {
bucket, access, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
bucket, acct, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("account").(auth.Account), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "HeadBucket", BucketOwner: parsedAcl.Owner})
}
@@ -775,14 +775,14 @@ const (
)
func (c S3ApiController) HeadObject(ctx *fiber.Ctx) error {
bucket, access, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("access").(string), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
bucket, acct, isRoot, parsedAcl := ctx.Params("bucket"), ctx.Locals("account").(auth.Account), ctx.Locals("isRoot").(bool), ctx.Locals("parsedAcl").(auth.ACL)
key := ctx.Params("key")
keyEnd := ctx.Params("*1")
if keyEnd != "" {
key = strings.Join([]string{key, keyEnd}, "/")
}
if err := auth.VerifyACL(parsedAcl, access, "READ", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "READ", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "HeadObject", BucketOwner: parsedAcl.Owner})
}
@@ -841,7 +841,7 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
key := ctx.Params("key")
keyEnd := ctx.Params("*1")
uploadId := ctx.Query("uploadId")
access := ctx.Locals("access").(string)
acct := ctx.Locals("account").(auth.Account)
isRoot := ctx.Locals("isRoot").(bool)
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
@@ -856,7 +856,7 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "RestoreObject", BucketOwner: parsedAcl.Owner})
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "RestoreObject", BucketOwner: parsedAcl.Owner})
}
@@ -884,7 +884,7 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
})
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "SelectObjectContent", BucketOwner: parsedAcl.Owner})
}
@@ -914,7 +914,7 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
})
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "CompleteMultipartUpload", BucketOwner: parsedAcl.Owner})
}
@@ -945,7 +945,7 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error {
}
}
if err := auth.VerifyACL(parsedAcl, access, "WRITE", isRoot); err != nil {
if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil {
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "CreateMultipartUpload", BucketOwner: parsedAcl.Owner})
}

View File

@@ -98,8 +98,7 @@ func TestS3ApiController_ListBuckets(t *testing.T) {
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "valid access", Role: "admin:"})
ctx.Locals("isDebug", false)
return ctx.Next()
})
@@ -116,8 +115,7 @@ func TestS3ApiController_ListBuckets(t *testing.T) {
}
appErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("role", "admin")
ctx.Locals("account", auth.Account{Access: "valid access", Role: "admin:"})
ctx.Locals("isDebug", false)
return ctx.Next()
})
@@ -207,7 +205,7 @@ func TestS3ApiController_GetActions(t *testing.T) {
},
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -347,7 +345,7 @@ func TestS3ApiController_ListActions(t *testing.T) {
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -369,7 +367,7 @@ func TestS3ApiController_ListActions(t *testing.T) {
}
appError := fiber.New()
appError.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -507,7 +505,7 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
}
// Mock ctx.Locals
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{Owner: "valid access"})
@@ -680,7 +678,7 @@ func TestS3ApiController_PutActions(t *testing.T) {
},
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -879,7 +877,7 @@ func TestS3ApiController_DeleteBucket(t *testing.T) {
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -936,7 +934,7 @@ func TestS3ApiController_DeleteObjects(t *testing.T) {
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -1013,7 +1011,7 @@ func TestS3ApiController_DeleteActions(t *testing.T) {
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -1034,7 +1032,7 @@ func TestS3ApiController_DeleteActions(t *testing.T) {
}}
appErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -1117,7 +1115,7 @@ func TestS3ApiController_HeadBucket(t *testing.T) {
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -1140,7 +1138,7 @@ func TestS3ApiController_HeadBucket(t *testing.T) {
}
appErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -1219,7 +1217,7 @@ func TestS3ApiController_HeadObject(t *testing.T) {
}
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -1242,7 +1240,7 @@ func TestS3ApiController_HeadObject(t *testing.T) {
}
appErr.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})
@@ -1322,7 +1320,7 @@ func TestS3ApiController_CreateActions(t *testing.T) {
`
app.Use(func(ctx *fiber.Ctx) error {
ctx.Locals("access", "valid access")
ctx.Locals("account", auth.Account{Access: "valid access"})
ctx.Locals("isRoot", true)
ctx.Locals("isDebug", false)
ctx.Locals("parsedAcl", auth.ACL{})

View File

@@ -18,7 +18,7 @@ var _ auth.IAMService = &IAMServiceMock{}
//
// // make and configure a mocked auth.IAMService
// mockedIAMService := &IAMServiceMock{
// CreateAccountFunc: func(access string, account auth.Account) error {
// CreateAccountFunc: func(account auth.Account) error {
// panic("mock out the CreateAccount method")
// },
// DeleteUserAccountFunc: func(access string) error {
@@ -38,7 +38,7 @@ var _ auth.IAMService = &IAMServiceMock{}
// }
type IAMServiceMock struct {
// CreateAccountFunc mocks the CreateAccount method.
CreateAccountFunc func(access string, account auth.Account) error
CreateAccountFunc func(account auth.Account) error
// DeleteUserAccountFunc mocks the DeleteUserAccount method.
DeleteUserAccountFunc func(access string) error
@@ -53,8 +53,6 @@ type IAMServiceMock struct {
calls struct {
// CreateAccount holds details about calls to the CreateAccount method.
CreateAccount []struct {
// Access is the access argument value.
Access string
// Account is the account argument value.
Account auth.Account
}
@@ -79,21 +77,19 @@ type IAMServiceMock struct {
}
// CreateAccount calls CreateAccountFunc.
func (mock *IAMServiceMock) CreateAccount(access string, account auth.Account) error {
func (mock *IAMServiceMock) CreateAccount(account auth.Account) error {
if mock.CreateAccountFunc == nil {
panic("IAMServiceMock.CreateAccountFunc: method is nil but IAMService.CreateAccount was just called")
}
callInfo := struct {
Access string
Account auth.Account
}{
Access: access,
Account: account,
}
mock.lockCreateAccount.Lock()
mock.calls.CreateAccount = append(mock.calls.CreateAccount, callInfo)
mock.lockCreateAccount.Unlock()
return mock.CreateAccountFunc(access, account)
return mock.CreateAccountFunc(account)
}
// CreateAccountCalls gets all the calls that were made to CreateAccount.
@@ -101,11 +97,9 @@ func (mock *IAMServiceMock) CreateAccount(access string, account auth.Account) e
//
// len(mockedIAMService.CreateAccountCalls())
func (mock *IAMServiceMock) CreateAccountCalls() []struct {
Access string
Account auth.Account
} {
var calls []struct {
Access string
Account auth.Account
}
mock.lockCreateAccount.RLock()

View File

@@ -28,7 +28,7 @@ import (
func AclParser(be backend.Backend, logger s3log.AuditLogger) fiber.Handler {
return func(ctx *fiber.Ctx) error {
isRoot, access := ctx.Locals("isRoot").(bool), ctx.Locals("access").(string)
isRoot, acct := ctx.Locals("isRoot").(bool), ctx.Locals("account").(auth.Account)
path := ctx.Path()
pathParts := strings.Split(path, "/")
bucket := pathParts[1]
@@ -39,7 +39,7 @@ func AclParser(be backend.Backend, logger s3log.AuditLogger) fiber.Handler {
return ctx.Next()
}
if len(pathParts) == 2 && pathParts[1] != "" && ctx.Method() == http.MethodPut && !ctx.Request().URI().QueryArgs().Has("acl") {
if err := auth.IsAdmin(access, isRoot); err != nil {
if err := auth.IsAdmin(acct, isRoot); err != nil {
return controllers.SendXMLResponse(ctx, nil, err, &controllers.MetaOpts{Logger: logger, Action: "CreateBucket"})
}
return ctx.Next()

View File

@@ -95,7 +95,6 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, logger s3log.Au
}, &controllers.MetaOpts{Logger: logger})
}
ctx.Locals("access", creds[0])
ctx.Locals("isRoot", creds[0] == root.Access)
_, err := time.Parse(YYYYMMDD, creds[1])
@@ -116,7 +115,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, logger s3log.Au
if err != nil {
return controllers.SendResponse(ctx, err, &controllers.MetaOpts{Logger: logger})
}
ctx.Locals("role", account.Role)
ctx.Locals("account", account)
// Check X-Amz-Date header
date := ctx.Get("X-Amz-Date")
@@ -199,6 +198,7 @@ type accounts struct {
func (a accounts) getAccount(access string) (auth.Account, error) {
if access == a.root.Access {
return auth.Account{
Access: a.root.Access,
Secret: a.root.Secret,
Role: "admin",
}, nil