mirror of
https://github.com/versity/versitygw.git
synced 2026-01-07 12:15:18 +00:00
Merge pull request #270 from versity/ben/iam_refactor
This commit is contained in:
22
auth/acl.go
22
auth/acl.go
@@ -17,7 +17,6 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"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)
|
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsAdmin(access string, isRoot bool) error {
|
func IsAdmin(acct Account, isRoot bool) error {
|
||||||
var data IAMConfig
|
|
||||||
|
|
||||||
if isRoot {
|
if isRoot {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile("users.json")
|
if acct.Role == "admin" {
|
||||||
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" {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
return s3err.GetAPIError(s3err.ErrAccessDenied)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type Account struct {
|
|||||||
//
|
//
|
||||||
//go:generate moq -out ../s3api/controllers/iam_moq_test.go -pkg controllers . IAMService
|
//go:generate moq -out ../s3api/controllers/iam_moq_test.go -pkg controllers . IAMService
|
||||||
type IAMService interface {
|
type IAMService interface {
|
||||||
CreateAccount(access string, account Account) error
|
CreateAccount(account Account) error
|
||||||
GetUserAccount(access string) (Account, error)
|
GetUserAccount(access string) (Account, error)
|
||||||
DeleteUserAccount(access string) error
|
DeleteUserAccount(access string) error
|
||||||
ListUserAccounts() ([]Account, error)
|
ListUserAccounts() ([]Account, error)
|
||||||
|
|||||||
@@ -16,45 +16,59 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
iamFile = "users.json"
|
||||||
|
iamBackupFile = "users.json.backup"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cacheDuration = 5 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// IAMServiceInternal manages the internal IAM service
|
// IAMServiceInternal manages the internal IAM service
|
||||||
type IAMServiceInternal struct {
|
type IAMServiceInternal struct {
|
||||||
storer Storer
|
path string
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
accts IAMConfig
|
accts iAMConfig
|
||||||
serial uint32
|
serial uint32
|
||||||
|
iamcache []byte
|
||||||
|
iamvalid bool
|
||||||
|
iamexpire time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAcctFunc accepts the current data and returns the new data to be stored
|
// UpdateAcctFunc accepts the current data and returns the new data to be stored
|
||||||
type UpdateAcctFunc func([]byte) ([]byte, error)
|
type UpdateAcctFunc func([]byte) ([]byte, error)
|
||||||
|
|
||||||
// Storer is the interface to manage the peristent IAM data for the internal
|
// iAMConfig stores all internal IAM accounts
|
||||||
// IAM service
|
type iAMConfig struct {
|
||||||
type Storer interface {
|
|
||||||
InitIAM() error
|
|
||||||
GetIAM() ([]byte, error)
|
|
||||||
StoreIAM(UpdateAcctFunc) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// IAMConfig stores all internal IAM accounts
|
|
||||||
type IAMConfig struct {
|
|
||||||
AccessAccounts map[string]Account `json:"accessAccounts"`
|
AccessAccounts map[string]Account `json:"accessAccounts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ IAMService = &IAMServiceInternal{}
|
var _ IAMService = &IAMServiceInternal{}
|
||||||
|
|
||||||
// NewInternal creates a new instance for the Internal IAM service
|
// NewInternal creates a new instance for the Internal IAM service
|
||||||
func NewInternal(s Storer) (*IAMServiceInternal, error) {
|
func NewInternal(path string) (*IAMServiceInternal, error) {
|
||||||
i := &IAMServiceInternal{
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("refresh iam cache: %w", err)
|
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
|
// CreateAccount creates a new IAM account. Returns an error if the account
|
||||||
// already exists.
|
// already exists.
|
||||||
func (s *IAMServiceInternal) CreateAccount(access string, account Account) error {
|
func (s *IAMServiceInternal) CreateAccount(account Account) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
return s.storer.StoreIAM(func(data []byte) ([]byte, error) {
|
return s.storeIAM(func(data []byte) ([]byte, error) {
|
||||||
var conf IAMConfig
|
var conf iAMConfig
|
||||||
|
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
if err := json.Unmarshal(data, &conf); err != nil {
|
if err := json.Unmarshal(data, &conf); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse iam: %w", err)
|
return nil, fmt.Errorf("failed to parse iam: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conf = IAMConfig{AccessAccounts: map[string]Account{}}
|
conf = iAMConfig{AccessAccounts: map[string]Account{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := conf.AccessAccounts[access]
|
_, ok := conf.AccessAccounts[account.Access]
|
||||||
if ok {
|
if ok {
|
||||||
return nil, fmt.Errorf("account already exists")
|
return nil, fmt.Errorf("account already exists")
|
||||||
}
|
}
|
||||||
conf.AccessAccounts[access] = account
|
conf.AccessAccounts[account.Access] = account
|
||||||
|
|
||||||
b, err := json.Marshal(conf)
|
b, err := json.Marshal(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -101,7 +115,7 @@ func (s *IAMServiceInternal) GetUserAccount(access string) (Account, error) {
|
|||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
|
|
||||||
data, err := s.storer.GetIAM()
|
data, err := s.getIAM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Account{}, fmt.Errorf("get iam data: %w", err)
|
return Account{}, fmt.Errorf("get iam data: %w", err)
|
||||||
}
|
}
|
||||||
@@ -129,7 +143,7 @@ func (s *IAMServiceInternal) updateCache() error {
|
|||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
data, err := s.storer.GetIAM()
|
data, err := s.getIAM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get iam data: %w", err)
|
return fmt.Errorf("get iam data: %w", err)
|
||||||
}
|
}
|
||||||
@@ -155,13 +169,13 @@ func (s *IAMServiceInternal) DeleteUserAccount(access string) error {
|
|||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
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 {
|
if len(data) == 0 {
|
||||||
// empty config, do nothing
|
// empty config, do nothing
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var conf IAMConfig
|
var conf iAMConfig
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &conf); err != nil {
|
if err := json.Unmarshal(data, &conf); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse iam: %w", err)
|
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()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
|
|
||||||
data, err := s.storer.GetIAM()
|
data, err := s.getIAM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []Account{}, fmt.Errorf("get iam data: %w", err)
|
return []Account{}, fmt.Errorf("get iam data: %w", err)
|
||||||
}
|
}
|
||||||
@@ -210,3 +224,186 @@ func (s *IAMServiceInternal) ListUserAccounts() (accs []Account, err error) {
|
|||||||
|
|
||||||
return accs, nil
|
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
40
auth/iam_single.go
Normal 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
|
||||||
|
}
|
||||||
@@ -29,9 +29,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||||
@@ -48,19 +46,10 @@ type Posix struct {
|
|||||||
|
|
||||||
rootfd *os.File
|
rootfd *os.File
|
||||||
rootdir string
|
rootdir string
|
||||||
|
|
||||||
mu sync.RWMutex
|
|
||||||
iamcache []byte
|
|
||||||
iamvalid bool
|
|
||||||
iamexpire time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ backend.Backend = &Posix{}
|
var _ backend.Backend = &Posix{}
|
||||||
|
|
||||||
var (
|
|
||||||
cacheDuration = 5 * time.Minute
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
metaTmpDir = ".sgwtmp"
|
metaTmpDir = ".sgwtmp"
|
||||||
metaTmpMultipartDir = metaTmpDir + "/multipart"
|
metaTmpMultipartDir = metaTmpDir + "/multipart"
|
||||||
@@ -70,8 +59,6 @@ const (
|
|||||||
contentTypeHdr = "content-type"
|
contentTypeHdr = "content-type"
|
||||||
contentEncHdr = "content-encoding"
|
contentEncHdr = "content-encoding"
|
||||||
emptyMD5 = "d41d8cd98f00b204e9800998ecf8427e"
|
emptyMD5 = "d41d8cd98f00b204e9800998ecf8427e"
|
||||||
iamFile = "users.json"
|
|
||||||
iamBackupFile = "users.json.backup"
|
|
||||||
aclkey = "user.acl"
|
aclkey = "user.acl"
|
||||||
etagkey = "user.etag"
|
etagkey = "user.etag"
|
||||||
)
|
)
|
||||||
@@ -148,6 +135,8 @@ func (p *Posix) ListBuckets(_ context.Context, owner string, isAdmin bool) (s3re
|
|||||||
|
|
||||||
sort.Sort(backend.ByBucketName(buckets))
|
sort.Sort(backend.ByBucketName(buckets))
|
||||||
|
|
||||||
|
fmt.Println("ListAllMyBucketsResult owner:", owner)
|
||||||
|
|
||||||
return s3response.ListAllMyBucketsResult{
|
return s3response.ListAllMyBucketsResult{
|
||||||
Buckets: s3response.ListAllMyBucketsList{
|
Buckets: s3response.ListAllMyBucketsList{
|
||||||
Bucket: buckets,
|
Bucket: buckets,
|
||||||
@@ -1654,198 +1643,6 @@ func (p *Posix) ListBucketsAndOwners(ctx context.Context) (buckets []s3response.
|
|||||||
return buckets, nil
|
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 {
|
func isNoAttr(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ var (
|
|||||||
logWebhookURL string
|
logWebhookURL string
|
||||||
accessLog string
|
accessLog string
|
||||||
debug bool
|
debug bool
|
||||||
|
iamDir string
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -207,10 +208,15 @@ func initFlags() []cli.Flag {
|
|||||||
Destination: &natsTopic,
|
Destination: &natsTopic,
|
||||||
Aliases: []string{"ent"},
|
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
|
// int32 max for 32 bit arch
|
||||||
blimit := int64(2*1024*1024*1024 - 1)
|
blimit := int64(2*1024*1024*1024 - 1)
|
||||||
if strconv.IntSize > 32 {
|
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))
|
admOpts = append(admOpts, s3api.WithAdminSrvTLS(cert))
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.InitIAM()
|
var iam auth.IAMService
|
||||||
if err != nil {
|
switch {
|
||||||
return fmt.Errorf("init iam: %w", err)
|
case iamDir != "":
|
||||||
}
|
var err error
|
||||||
|
iam, err = auth.NewInternal(iamDir)
|
||||||
iam, err := auth.NewInternal(s)
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("setup internal iam service: %w", err)
|
||||||
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{
|
logger, err := s3log.InitLogger(&s3log.LogConfig{
|
||||||
|
|||||||
@@ -49,5 +49,5 @@ func runPosix(ctx *cli.Context) error {
|
|||||||
return fmt.Errorf("init posix: %v", err)
|
return fmt.Errorf("init posix: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return runGateway(ctx, be, be)
|
return runGateway(ctx, be)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,5 +69,5 @@ func runScoutfs(ctx *cli.Context) error {
|
|||||||
return fmt.Errorf("init scoutfs: %v", err)
|
return fmt.Errorf("init scoutfs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return runGateway(ctx, be, be)
|
return runGateway(ctx, be)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ rm -rf /tmp/covdata
|
|||||||
mkdir /tmp/covdata
|
mkdir /tmp/covdata
|
||||||
|
|
||||||
# run server in background
|
# 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=$!
|
GW_PID=$!
|
||||||
|
|
||||||
# wait a second for server to start up
|
# wait a second for server to start up
|
||||||
|
|||||||
@@ -33,18 +33,18 @@ func NewAdminController(iam auth.IAMService, be backend.Backend) AdminController
|
|||||||
|
|
||||||
func (c AdminController) CreateUser(ctx *fiber.Ctx) error {
|
func (c AdminController) CreateUser(ctx *fiber.Ctx) error {
|
||||||
access, secret, role := ctx.Query("access"), ctx.Query("secret"), ctx.Query("role")
|
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")
|
return fmt.Errorf("access denied: only admin users have access to this resource")
|
||||||
}
|
}
|
||||||
if role != "user" && role != "admin" {
|
if role != "user" && role != "admin" {
|
||||||
return fmt.Errorf("invalid parameters: user role have to be one of the following: 'user', '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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create a user: %w", err)
|
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 {
|
func (c AdminController) DeleteUser(ctx *fiber.Ctx) error {
|
||||||
access := ctx.Query("access")
|
access := ctx.Query("access")
|
||||||
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")
|
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 {
|
func (c AdminController) ListUsers(ctx *fiber.Ctx) error {
|
||||||
role := ctx.Locals("role").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
if role != "admin" {
|
if acct.Role != "admin" {
|
||||||
return fmt.Errorf("access denied: only admin users have access to this resource")
|
return fmt.Errorf("access denied: only admin users have access to this resource")
|
||||||
}
|
}
|
||||||
accs, err := c.iam.ListUserAccounts()
|
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 {
|
func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) error {
|
||||||
role := ctx.Locals("role").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
if role != "admin" {
|
if acct.Role != "admin" {
|
||||||
return fmt.Errorf("access denied: only admin users have access to this resource")
|
return fmt.Errorf("access denied: only admin users have access to this resource")
|
||||||
}
|
}
|
||||||
owner := ctx.Query("owner")
|
owner := ctx.Query("owner")
|
||||||
@@ -105,8 +105,8 @@ func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c AdminController) ListBuckets(ctx *fiber.Ctx) error {
|
func (c AdminController) ListBuckets(ctx *fiber.Ctx) error {
|
||||||
role := ctx.Locals("role").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
if role != "admin" {
|
if acct.Role != "admin" {
|
||||||
return fmt.Errorf("access denied: only admin users have access to this resource")
|
return fmt.Errorf("access denied: only admin users have access to this resource")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func TestAdminController_CreateUser(t *testing.T) {
|
|||||||
|
|
||||||
adminController := AdminController{
|
adminController := AdminController{
|
||||||
iam: &IAMServiceMock{
|
iam: &IAMServiceMock{
|
||||||
CreateAccountFunc: func(access string, account auth.Account) error {
|
CreateAccountFunc: func(account auth.Account) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -42,7 +42,7 @@ func TestAdminController_CreateUser(t *testing.T) {
|
|||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ func TestAdminController_CreateUser(t *testing.T) {
|
|||||||
appErr := fiber.New()
|
appErr := fiber.New()
|
||||||
|
|
||||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ func TestAdminController_DeleteUser(t *testing.T) {
|
|||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ func TestAdminController_DeleteUser(t *testing.T) {
|
|||||||
appErr := fiber.New()
|
appErr := fiber.New()
|
||||||
|
|
||||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ func TestAdminController_ListUsers(t *testing.T) {
|
|||||||
appErr := fiber.New()
|
appErr := fiber.New()
|
||||||
|
|
||||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ func TestAdminController_ListUsers(t *testing.T) {
|
|||||||
appRoleErr := fiber.New()
|
appRoleErr := fiber.New()
|
||||||
|
|
||||||
appRoleErr.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ func TestAdminController_ListUsers(t *testing.T) {
|
|||||||
appSucc := fiber.New()
|
appSucc := fiber.New()
|
||||||
|
|
||||||
appSucc.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
|
|||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
|
|||||||
appRoleErr := fiber.New()
|
appRoleErr := fiber.New()
|
||||||
|
|
||||||
appRoleErr.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -325,7 +325,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
|
|||||||
appIamErr := fiber.New()
|
appIamErr := fiber.New()
|
||||||
|
|
||||||
appIamErr.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) {
|
|||||||
appIamNoSuchUser := fiber.New()
|
appIamNoSuchUser := fiber.New()
|
||||||
|
|
||||||
appIamNoSuchUser.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -412,7 +412,7 @@ func TestAdminController_ListBuckets(t *testing.T) {
|
|||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -421,7 +421,7 @@ func TestAdminController_ListBuckets(t *testing.T) {
|
|||||||
appRoleErr := fiber.New()
|
appRoleErr := fiber.New()
|
||||||
|
|
||||||
appRoleErr.Use(func(ctx *fiber.Ctx) error {
|
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()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ func New(be backend.Backend, iam auth.IAMService, logger s3log.AuditLogger, evs
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) error {
|
func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) error {
|
||||||
access, role := ctx.Locals("access").(string), ctx.Locals("role").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
res, err := c.be.ListBuckets(ctx.Context(), access, role == "admin")
|
res, err := c.be.ListBuckets(ctx.Context(), acct.Access, acct.Role == "admin")
|
||||||
return SendXMLResponse(ctx, res, err, &MetaOpts{Logger: c.logger, Action: "ListBucket"})
|
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)
|
maxParts := ctx.QueryInt("max-parts", 0)
|
||||||
partNumberMarker := ctx.Query("part-number-marker")
|
partNumberMarker := ctx.Query("part-number-marker")
|
||||||
acceptRange := ctx.Get("Range")
|
acceptRange := ctx.Get("Range")
|
||||||
access := ctx.Locals("access").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
isRoot := ctx.Locals("isRoot").(bool)
|
isRoot := ctx.Locals("isRoot").(bool)
|
||||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||||
if keyEnd != "" {
|
if keyEnd != "" {
|
||||||
@@ -74,7 +74,7 @@ func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Request().URI().QueryArgs().Has("tagging") {
|
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})
|
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})
|
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 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})
|
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "GetObjectAcl", BucketOwner: parsedAcl.Owner})
|
||||||
}
|
}
|
||||||
res, err := c.be.GetObjectAcl(ctx.Context(), &s3.GetObjectAclInput{
|
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 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})
|
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "GetObjectAttributes", BucketOwner: parsedAcl.Owner})
|
||||||
}
|
}
|
||||||
var oattrs []types.ObjectAttributes
|
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})
|
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})
|
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")
|
keyMarker := ctx.Query("key-marker")
|
||||||
maxUploadsStr := ctx.Query("max-uploads")
|
maxUploadsStr := ctx.Query("max-uploads")
|
||||||
uploadIdMarker := ctx.Query("upload-id-marker")
|
uploadIdMarker := ctx.Query("upload-id-marker")
|
||||||
access := ctx.Locals("access").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
isRoot := ctx.Locals("isRoot").(bool)
|
isRoot := ctx.Locals("isRoot").(bool)
|
||||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||||
|
|
||||||
if ctx.Request().URI().QueryArgs().Has("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})
|
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 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})
|
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "ListMultipartUploads", BucketOwner: parsedAcl.Owner})
|
||||||
}
|
}
|
||||||
maxUploads, err := utils.ParseUint(maxUploadsStr)
|
maxUploads, err := utils.ParseUint(maxUploadsStr)
|
||||||
@@ -266,7 +266,7 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ctx.QueryInt("list-type") == 2 {
|
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})
|
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "ListObjectsV2", BucketOwner: parsedAcl.Owner})
|
||||||
}
|
}
|
||||||
maxkeys, err := utils.ParseUint(maxkeysStr)
|
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})
|
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})
|
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 {
|
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.Params("bucket"),
|
||||||
ctx.Get("X-Amz-Acl"),
|
ctx.Get("X-Amz-Acl"),
|
||||||
ctx.Get("X-Amz-Grant-Full-Control"),
|
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-Read-Acp"),
|
||||||
ctx.Get("X-Amz-Grant-Write"),
|
ctx.Get("X-Amz-Grant-Write"),
|
||||||
ctx.Get("X-Amz-Grant-Write-Acp"),
|
ctx.Get("X-Amz-Grant-Write-Acp"),
|
||||||
ctx.Locals("access").(string),
|
ctx.Locals("account").(auth.Account),
|
||||||
ctx.Locals("isRoot").(bool)
|
ctx.Locals("isRoot").(bool)
|
||||||
|
|
||||||
grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP
|
grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP
|
||||||
@@ -329,7 +329,7 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
|
|||||||
var accessControlPolicy auth.AccessControlPolicy
|
var accessControlPolicy auth.AccessControlPolicy
|
||||||
|
|
||||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
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})
|
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{
|
err := c.be.CreateBucket(ctx.Context(), &s3.CreateBucketInput{
|
||||||
Bucket: &bucket,
|
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 {
|
func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
||||||
@@ -401,7 +401,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
|||||||
keyStart := ctx.Params("key")
|
keyStart := ctx.Params("key")
|
||||||
keyEnd := ctx.Params("*1")
|
keyEnd := ctx.Params("*1")
|
||||||
uploadId := ctx.Query("uploadId")
|
uploadId := ctx.Query("uploadId")
|
||||||
access := ctx.Locals("access").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
isRoot := ctx.Locals("isRoot").(bool)
|
isRoot := ctx.Locals("isRoot").(bool)
|
||||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
||||||
tagging := ctx.Get("x-amz-tagging")
|
tagging := ctx.Get("x-amz-tagging")
|
||||||
@@ -452,7 +452,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
|||||||
tags[tag.Key] = tag.Value
|
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})
|
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})
|
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})
|
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 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})
|
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: ©SrcIfNoneMatch,
|
CopySourceIfNoneMatch: ©SrcIfNoneMatch,
|
||||||
CopySourceIfModifiedSince: &mtime,
|
CopySourceIfModifiedSince: &mtime,
|
||||||
CopySourceIfUnmodifiedSince: &umtime,
|
CopySourceIfUnmodifiedSince: &umtime,
|
||||||
ExpectedBucketOwner: &access,
|
ExpectedBucketOwner: &acct.Access,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -628,7 +628,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
|
|||||||
|
|
||||||
metadata := utils.GetUserMetaData(&ctx.Request().Header)
|
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})
|
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 {
|
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})
|
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 {
|
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
|
var dObj s3response.DeleteObjects
|
||||||
|
|
||||||
if err := xml.Unmarshal(ctx.Body(), &dObj); err != nil {
|
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})
|
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})
|
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")
|
key := ctx.Params("key")
|
||||||
keyEnd := ctx.Params("*1")
|
keyEnd := ctx.Params("*1")
|
||||||
uploadId := ctx.Query("uploadId")
|
uploadId := ctx.Query("uploadId")
|
||||||
access := ctx.Locals("access").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
isRoot := ctx.Locals("isRoot").(bool)
|
isRoot := ctx.Locals("isRoot").(bool)
|
||||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
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 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})
|
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 != "" {
|
if uploadId != "" {
|
||||||
expectedBucketOwner, requestPayer := ctx.Get("X-Amz-Expected-Bucket-Owner"), ctx.Get("X-Amz-Request-Payer")
|
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})
|
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})
|
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})
|
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 {
|
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})
|
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 {
|
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")
|
key := ctx.Params("key")
|
||||||
keyEnd := ctx.Params("*1")
|
keyEnd := ctx.Params("*1")
|
||||||
if keyEnd != "" {
|
if keyEnd != "" {
|
||||||
key = strings.Join([]string{key, 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})
|
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")
|
key := ctx.Params("key")
|
||||||
keyEnd := ctx.Params("*1")
|
keyEnd := ctx.Params("*1")
|
||||||
uploadId := ctx.Query("uploadId")
|
uploadId := ctx.Query("uploadId")
|
||||||
access := ctx.Locals("access").(string)
|
acct := ctx.Locals("account").(auth.Account)
|
||||||
isRoot := ctx.Locals("isRoot").(bool)
|
isRoot := ctx.Locals("isRoot").(bool)
|
||||||
parsedAcl := ctx.Locals("parsedAcl").(auth.ACL)
|
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})
|
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})
|
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})
|
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})
|
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})
|
return SendXMLResponse(ctx, nil, err, &MetaOpts{Logger: c.logger, Action: "CreateMultipartUpload", BucketOwner: parsedAcl.Owner})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,8 +98,7 @@ func TestS3ApiController_ListBuckets(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
app.Use(func(ctx *fiber.Ctx) error {
|
||||||
ctx.Locals("access", "valid access")
|
ctx.Locals("account", auth.Account{Access: "valid access", Role: "admin:"})
|
||||||
ctx.Locals("role", "admin")
|
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
return ctx.Next()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
@@ -116,8 +115,7 @@ func TestS3ApiController_ListBuckets(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
appErr.Use(func(ctx *fiber.Ctx) error {
|
||||||
ctx.Locals("access", "valid access")
|
ctx.Locals("account", auth.Account{Access: "valid access", Role: "admin:"})
|
||||||
ctx.Locals("role", "admin")
|
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
return ctx.Next()
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
@@ -207,7 +205,7 @@ func TestS3ApiController_GetActions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -347,7 +345,7 @@ func TestS3ApiController_ListActions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -369,7 +367,7 @@ func TestS3ApiController_ListActions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
appError := fiber.New()
|
appError := fiber.New()
|
||||||
appError.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -507,7 +505,7 @@ func TestS3ApiController_PutBucketActions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// Mock ctx.Locals
|
// Mock ctx.Locals
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{Owner: "valid access"})
|
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 {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -879,7 +877,7 @@ func TestS3ApiController_DeleteBucket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -936,7 +934,7 @@ func TestS3ApiController_DeleteObjects(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -1013,7 +1011,7 @@ func TestS3ApiController_DeleteActions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -1034,7 +1032,7 @@ func TestS3ApiController_DeleteActions(t *testing.T) {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -1117,7 +1115,7 @@ func TestS3ApiController_HeadBucket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -1140,7 +1138,7 @@ func TestS3ApiController_HeadBucket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -1219,7 +1217,7 @@ func TestS3ApiController_HeadObject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -1242,7 +1240,7 @@ func TestS3ApiController_HeadObject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appErr.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
@@ -1322,7 +1320,7 @@ func TestS3ApiController_CreateActions(t *testing.T) {
|
|||||||
`
|
`
|
||||||
|
|
||||||
app.Use(func(ctx *fiber.Ctx) error {
|
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("isRoot", true)
|
||||||
ctx.Locals("isDebug", false)
|
ctx.Locals("isDebug", false)
|
||||||
ctx.Locals("parsedAcl", auth.ACL{})
|
ctx.Locals("parsedAcl", auth.ACL{})
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ var _ auth.IAMService = &IAMServiceMock{}
|
|||||||
//
|
//
|
||||||
// // make and configure a mocked auth.IAMService
|
// // make and configure a mocked auth.IAMService
|
||||||
// mockedIAMService := &IAMServiceMock{
|
// mockedIAMService := &IAMServiceMock{
|
||||||
// CreateAccountFunc: func(access string, account auth.Account) error {
|
// CreateAccountFunc: func(account auth.Account) error {
|
||||||
// panic("mock out the CreateAccount method")
|
// panic("mock out the CreateAccount method")
|
||||||
// },
|
// },
|
||||||
// DeleteUserAccountFunc: func(access string) error {
|
// DeleteUserAccountFunc: func(access string) error {
|
||||||
@@ -38,7 +38,7 @@ var _ auth.IAMService = &IAMServiceMock{}
|
|||||||
// }
|
// }
|
||||||
type IAMServiceMock struct {
|
type IAMServiceMock struct {
|
||||||
// CreateAccountFunc mocks the CreateAccount method.
|
// 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 mocks the DeleteUserAccount method.
|
||||||
DeleteUserAccountFunc func(access string) error
|
DeleteUserAccountFunc func(access string) error
|
||||||
@@ -53,8 +53,6 @@ type IAMServiceMock struct {
|
|||||||
calls struct {
|
calls struct {
|
||||||
// CreateAccount holds details about calls to the CreateAccount method.
|
// CreateAccount holds details about calls to the CreateAccount method.
|
||||||
CreateAccount []struct {
|
CreateAccount []struct {
|
||||||
// Access is the access argument value.
|
|
||||||
Access string
|
|
||||||
// Account is the account argument value.
|
// Account is the account argument value.
|
||||||
Account auth.Account
|
Account auth.Account
|
||||||
}
|
}
|
||||||
@@ -79,21 +77,19 @@ type IAMServiceMock struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccount calls CreateAccountFunc.
|
// 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 {
|
if mock.CreateAccountFunc == nil {
|
||||||
panic("IAMServiceMock.CreateAccountFunc: method is nil but IAMService.CreateAccount was just called")
|
panic("IAMServiceMock.CreateAccountFunc: method is nil but IAMService.CreateAccount was just called")
|
||||||
}
|
}
|
||||||
callInfo := struct {
|
callInfo := struct {
|
||||||
Access string
|
|
||||||
Account auth.Account
|
Account auth.Account
|
||||||
}{
|
}{
|
||||||
Access: access,
|
|
||||||
Account: account,
|
Account: account,
|
||||||
}
|
}
|
||||||
mock.lockCreateAccount.Lock()
|
mock.lockCreateAccount.Lock()
|
||||||
mock.calls.CreateAccount = append(mock.calls.CreateAccount, callInfo)
|
mock.calls.CreateAccount = append(mock.calls.CreateAccount, callInfo)
|
||||||
mock.lockCreateAccount.Unlock()
|
mock.lockCreateAccount.Unlock()
|
||||||
return mock.CreateAccountFunc(access, account)
|
return mock.CreateAccountFunc(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccountCalls gets all the calls that were made to CreateAccount.
|
// 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())
|
// len(mockedIAMService.CreateAccountCalls())
|
||||||
func (mock *IAMServiceMock) CreateAccountCalls() []struct {
|
func (mock *IAMServiceMock) CreateAccountCalls() []struct {
|
||||||
Access string
|
|
||||||
Account auth.Account
|
Account auth.Account
|
||||||
} {
|
} {
|
||||||
var calls []struct {
|
var calls []struct {
|
||||||
Access string
|
|
||||||
Account auth.Account
|
Account auth.Account
|
||||||
}
|
}
|
||||||
mock.lockCreateAccount.RLock()
|
mock.lockCreateAccount.RLock()
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
func AclParser(be backend.Backend, logger s3log.AuditLogger) fiber.Handler {
|
func AclParser(be backend.Backend, logger s3log.AuditLogger) fiber.Handler {
|
||||||
return func(ctx *fiber.Ctx) error {
|
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()
|
path := ctx.Path()
|
||||||
pathParts := strings.Split(path, "/")
|
pathParts := strings.Split(path, "/")
|
||||||
bucket := pathParts[1]
|
bucket := pathParts[1]
|
||||||
@@ -39,7 +39,7 @@ func AclParser(be backend.Backend, logger s3log.AuditLogger) fiber.Handler {
|
|||||||
return ctx.Next()
|
return ctx.Next()
|
||||||
}
|
}
|
||||||
if len(pathParts) == 2 && pathParts[1] != "" && ctx.Method() == http.MethodPut && !ctx.Request().URI().QueryArgs().Has("acl") {
|
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 controllers.SendXMLResponse(ctx, nil, err, &controllers.MetaOpts{Logger: logger, Action: "CreateBucket"})
|
||||||
}
|
}
|
||||||
return ctx.Next()
|
return ctx.Next()
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, logger s3log.Au
|
|||||||
}, &controllers.MetaOpts{Logger: logger})
|
}, &controllers.MetaOpts{Logger: logger})
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Locals("access", creds[0])
|
|
||||||
ctx.Locals("isRoot", creds[0] == root.Access)
|
ctx.Locals("isRoot", creds[0] == root.Access)
|
||||||
|
|
||||||
_, err := time.Parse(YYYYMMDD, creds[1])
|
_, err := time.Parse(YYYYMMDD, creds[1])
|
||||||
@@ -116,7 +115,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, logger s3log.Au
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return controllers.SendResponse(ctx, err, &controllers.MetaOpts{Logger: logger})
|
return controllers.SendResponse(ctx, err, &controllers.MetaOpts{Logger: logger})
|
||||||
}
|
}
|
||||||
ctx.Locals("role", account.Role)
|
ctx.Locals("account", account)
|
||||||
|
|
||||||
// Check X-Amz-Date header
|
// Check X-Amz-Date header
|
||||||
date := ctx.Get("X-Amz-Date")
|
date := ctx.Get("X-Amz-Date")
|
||||||
@@ -199,6 +198,7 @@ type accounts struct {
|
|||||||
func (a accounts) getAccount(access string) (auth.Account, error) {
|
func (a accounts) getAccount(access string) (auth.Account, error) {
|
||||||
if access == a.root.Access {
|
if access == a.root.Access {
|
||||||
return auth.Account{
|
return auth.Account{
|
||||||
|
Access: a.root.Access,
|
||||||
Secret: a.root.Secret,
|
Secret: a.root.Secret,
|
||||||
Role: "admin",
|
Role: "admin",
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
Reference in New Issue
Block a user