refactor iam service for blind backend store

This commit is contained in:
Ben McClelland
2023-06-19 09:53:19 -07:00
parent e7f6f76fb4
commit 94808bb4a9
12 changed files with 260 additions and 195 deletions
+7 -4
View File
@@ -76,7 +76,7 @@ func ParseACLOutput(data []byte) (GetBucketAclOutput, error) {
}, nil
}
func UpdateACL(input *s3.PutBucketAclInput, acl ACL, iam IAMConfig) error {
func UpdateACL(input *s3.PutBucketAclInput, acl ACL, iam IAMService) error {
if acl.Owner != *input.AccessControlPolicy.Owner.ID {
return s3err.GetAPIError(s3err.ErrAccessDenied)
}
@@ -141,12 +141,15 @@ func UpdateACL(input *s3.PutBucketAclInput, acl ACL, iam IAMConfig) error {
return nil
}
func checkIfAccountsExist(accs []string, iam IAMConfig) ([]string, error) {
func checkIfAccountsExist(accs []string, iam IAMService) ([]string, error) {
result := []string{}
for _, acc := range accs {
_, ok := iam.AccessAccounts[acc]
if !ok {
_, err := iam.GetUserAccount(acc)
if err != nil && err != ErrNoSuchUser {
return nil, fmt.Errorf("check user account: %w", err)
}
if err == nil {
result = append(result, acc)
}
}
+129 -137
View File
@@ -16,187 +16,179 @@ package auth
import (
"encoding/json"
"errors"
"fmt"
"os"
"hash/crc32"
"sync"
"github.com/versity/versitygw/s3err"
)
// Account is an internal IAM account
type Account struct {
Secret string `json:"secret"`
Role string `json:"role"`
Region string `json:"region"`
}
// 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 {
AccessAccounts map[string]Account `json:"accessAccounts"`
}
type AccountsCache struct {
mu sync.Mutex
Accounts map[string]Account
}
func (c *AccountsCache) getAccount(access string) *Account {
c.mu.Lock()
defer c.mu.Unlock()
acc, ok := c.Accounts[access]
if !ok {
return nil
}
return &acc
}
func (c *AccountsCache) updateAccounts() error {
c.mu.Lock()
defer c.mu.Unlock()
var data IAMConfig
file, err := os.ReadFile("users.json")
if err != nil {
return fmt.Errorf("error reading config file: %w", err)
}
if err := json.Unmarshal(file, &data); err != nil {
return fmt.Errorf("error parsing the data: %w", err)
}
c.Accounts = data.AccessAccounts
return nil
}
func (c *AccountsCache) deleteAccount(access string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.Accounts, access)
}
// IAMService is the interface for all IAM service implementations
type IAMService interface {
GetIAMConfig() (*IAMConfig, error)
CreateAccount(access string, account *Account) error
GetUserAccount(access string) *Account
CreateAccount(access string, account Account) error
GetUserAccount(access string) (Account, error)
DeleteUserAccount(access string) error
}
type IAMServiceUnsupported struct {
accCache *AccountsCache
// IAMServiceInternal manages the internal IAM service
type IAMServiceInternal struct {
storer Storer
mu sync.RWMutex
accts IAMConfig
serial uint32
}
var _ IAMService = &IAMServiceUnsupported{}
var _ IAMService = &IAMServiceInternal{}
func InitIAM() (IAMService, error) {
_, err := os.ReadFile("users.json")
// NewInternal creates a new instance for the Internal IAM service
func NewInternal(s Storer) (*IAMServiceInternal, error) {
i := &IAMServiceInternal{
storer: s,
}
err := i.updateCache()
if err != nil {
jsonData, err := json.MarshalIndent(IAMConfig{AccessAccounts: map[string]Account{}}, "", " ")
return nil, fmt.Errorf("refresh iam cache: %w", err)
}
return i, nil
}
// CreateAccount creates a new IAM account. Returns an error if the account
// already exists.
func (s *IAMServiceInternal) CreateAccount(access string, account Account) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.storer.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.AccessAccounts = make(map[string]Account)
}
_, ok := conf.AccessAccounts[access]
if ok {
return nil, fmt.Errorf("account already exists")
}
conf.AccessAccounts[access] = account
b, err := json.Marshal(s.accts)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to serialize iam: %w", err)
}
if err := os.WriteFile("users.json", jsonData, 0644); err != nil {
return nil, err
}
}
return &IAMServiceUnsupported{accCache: &AccountsCache{Accounts: map[string]Account{}}}, nil
return b, nil
})
}
func (IAMServiceUnsupported) GetIAMConfig() (*IAMConfig, error) {
return nil, s3err.GetAPIError(s3err.ErrNotImplemented)
}
var ErrNoSuchUser = errors.New("user not found")
func GetIAMConfig() (*IAMConfig, error) {
var data IAMConfig
// GetUserAccount retrieves account info for the requested user. Returns
// ErrNoSuchUser if the account does not exist.
func (s *IAMServiceInternal) GetUserAccount(access string) (Account, error) {
s.mu.RLock()
defer s.mu.RUnlock()
file, err := os.ReadFile("users.json")
data, err := s.storer.GetIAM()
if err != nil {
return nil, fmt.Errorf("unable to read config file: %w", err)
return Account{}, fmt.Errorf("get iam data: %w", err)
}
if err := json.Unmarshal(file, &data); err != nil {
return nil, err
}
return &data, nil
}
func (s IAMServiceUnsupported) CreateAccount(access string, account *Account) error {
var data IAMConfig
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
}
_, ok := data.AccessAccounts[access]
if ok {
return fmt.Errorf("user with the given access already exists")
}
data.AccessAccounts[access] = *account
updatedJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
return err
}
if err := os.WriteFile("users.json", updatedJSON, 0644); err != nil {
return err
}
return nil
}
func (s IAMServiceUnsupported) GetUserAccount(access string) *Account {
acc := s.accCache.getAccount(access)
if acc == nil {
err := s.accCache.updateAccounts()
serial := crc32.ChecksumIEEE(data)
if serial != s.serial {
s.mu.RUnlock()
err := s.updateCache()
s.mu.RUnlock()
if err != nil {
return nil
return Account{}, fmt.Errorf("refresh iam cache: %w", err)
}
return s.accCache.getAccount(access)
}
return acc
}
func (s IAMServiceUnsupported) DeleteUserAccount(access string) error {
var data IAMConfig
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 fmt.Errorf("failed to parse the config file: %w", err)
}
_, ok := data.AccessAccounts[access]
acct, ok := s.accts.AccessAccounts[access]
if !ok {
return fmt.Errorf("invalid access for the user: user does not exist")
return Account{}, ErrNoSuchUser
}
delete(data.AccessAccounts, access)
return acct, nil
}
updatedJSON, err := json.MarshalIndent(data, "", " ")
// updateCache must be called with no locks held
func (s *IAMServiceInternal) updateCache() error {
s.mu.Lock()
defer s.mu.Unlock()
data, err := s.storer.GetIAM()
if err != nil {
return fmt.Errorf("failed to parse the data: %w", err)
return fmt.Errorf("get iam data: %w", err)
}
if err := os.WriteFile("users.json", updatedJSON, 0644); err != nil {
return fmt.Errorf("failed to saved the changes: %w", err)
serial := crc32.ChecksumIEEE(data)
if len(data) > 0 {
if err := json.Unmarshal(data, &s.accts); err != nil {
return fmt.Errorf("failed to parse the config file: %w", err)
}
} else {
s.accts.AccessAccounts = make(map[string]Account)
}
s.accCache.deleteAccount(access)
s.serial = serial
return nil
}
// DeleteUserAccount deletes the specified user account. Does not check if
// account exists.
func (s *IAMServiceInternal) DeleteUserAccount(access string) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.storer.StoreIAM(func(data []byte) ([]byte, error) {
if len(data) == 0 {
// empty config, do nothing
return data, nil
}
var conf IAMConfig
if err := json.Unmarshal(data, &conf); err != nil {
return nil, fmt.Errorf("failed to parse iam: %w", err)
}
delete(conf.AccessAccounts, access)
b, err := json.Marshal(s.accts)
if err != nil {
return nil, fmt.Errorf("failed to serialize iam: %w", err)
}
return b, nil
})
}
+97 -21
View File
@@ -28,7 +28,9 @@ 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"
@@ -41,19 +43,34 @@ import (
)
type Posix struct {
backend.BackendUnsupported
rootfd *os.File
rootdir string
backend.BackendUnsupported
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"
onameAttr = "user.objname"
tagHdr = "X-Amz-Tagging"
contentTypeHdr = "content-type"
contentEncHdr = "content-encoding"
emptyMD5 = "d41d8cd98f00b204e9800998ecf8427e"
iamkey = "user.iam"
aclkey = "user.acl"
etagkey = "user.etag"
)
func New(rootdir string) (*Posix, error) {
@@ -140,7 +157,7 @@ func (p *Posix) PutBucket(bucket string, owner string) error {
return fmt.Errorf("marshal acl: %w", err)
}
if err := xattr.Set(bucket, "user.acl", jsonACL); err != nil {
if err := xattr.Set(bucket, aclkey, jsonACL); err != nil {
return fmt.Errorf("set acl: %w", err)
}
@@ -263,7 +280,7 @@ func (p *Posix) CompleteMultipartUpload(bucket, object, uploadID string, parts [
return nil, s3err.GetAPIError(s3err.ErrInvalidPart)
}
b, err := xattr.Get(partPath, "user.etag")
b, err := xattr.Get(partPath, etagkey)
etag := string(b)
if err != nil {
etag = ""
@@ -319,7 +336,7 @@ func (p *Posix) CompleteMultipartUpload(bucket, object, uploadID string, parts [
// Calculate s3 compatible md5sum for complete multipart.
s3MD5 := backend.GetMultipartMD5(parts)
err = xattr.Set(objname, "user.etag", []byte(s3MD5))
err = xattr.Set(objname, etagkey, []byte(s3MD5))
if err != nil {
// cleanup object if returning error
os.Remove(objname)
@@ -373,22 +390,22 @@ func loadUserMetaData(path string, m map[string]string) (contentType, contentEnc
m[strings.TrimPrefix(e, "user.")] = string(b)
}
b, err := xattr.Get(path, "user.content-type")
b, err := xattr.Get(path, "user."+contentTypeHdr)
contentType = string(b)
if err != nil {
contentType = ""
}
if contentType != "" {
m["content-type"] = contentType
m[contentTypeHdr] = contentType
}
b, err = xattr.Get(path, "user.content-encoding")
b, err = xattr.Get(path, "user."+contentEncHdr)
contentEncoding = string(b)
if err != nil {
contentEncoding = ""
}
if contentEncoding != "" {
m["content-encoding"] = contentEncoding
m[contentEncHdr] = contentEncoding
}
return
@@ -626,7 +643,7 @@ func (p *Posix) ListObjectParts(bucket, object, uploadID string, partNumberMarke
}
partPath := filepath.Join(objdir, uploadID, e.Name())
b, err := xattr.Get(partPath, "user.etag")
b, err := xattr.Get(partPath, etagkey)
etag := string(b)
if err != nil {
etag = ""
@@ -713,7 +730,7 @@ func (p *Posix) PutObjectPart(bucket, object, uploadID string, part int, length
dataSum := hash.Sum(nil)
etag := hex.EncodeToString(dataSum)
xattr.Set(partPath, "user.etag", []byte(etag))
xattr.Set(partPath, etagkey, []byte(etag))
return etag, nil
}
@@ -741,7 +758,7 @@ func (p *Posix) PutObject(po *s3.PutObjectInput) (string, error) {
}
// set etag attribute to signify this dir was specifically put
xattr.Set(name, "user.etag", []byte(emptyMD5))
xattr.Set(name, etagkey, []byte(emptyMD5))
return emptyMD5, nil
}
@@ -779,7 +796,7 @@ func (p *Posix) PutObject(po *s3.PutObjectInput) (string, error) {
dataSum := hash.Sum(nil)
etag := hex.EncodeToString(dataSum[:])
xattr.Set(name, "user.etag", []byte(etag))
xattr.Set(name, etagkey, []byte(etag))
return etag, nil
}
@@ -819,7 +836,7 @@ func (p *Posix) removeParents(bucket, object string) error {
break
}
_, err := xattr.Get(parent, "user.etag")
_, err := xattr.Get(parent, etagkey)
if err == nil {
break
}
@@ -893,7 +910,7 @@ func (p *Posix) GetObject(bucket, object, acceptRange string, writer io.Writer)
contentType, contentEncoding := loadUserMetaData(objPath, userMetaData)
b, err := xattr.Get(objPath, "user.etag")
b, err := xattr.Get(objPath, etagkey)
etag := string(b)
if err != nil {
etag = ""
@@ -937,7 +954,7 @@ func (p *Posix) HeadObject(bucket, object string) (*s3.HeadObjectOutput, error)
userMetaData := make(map[string]string)
contentType, contentEncoding := loadUserMetaData(objPath, userMetaData)
b, err := xattr.Get(objPath, "user.etag")
b, err := xattr.Get(objPath, etagkey)
etag := string(b)
if err != nil {
etag = ""
@@ -1010,7 +1027,7 @@ func (p *Posix) ListObjects(bucket, prefix, marker, delim string, maxkeys int) (
fileSystem := os.DirFS(bucket)
results, err := backend.Walk(fileSystem, prefix, delim, marker, maxkeys,
func(path string) (bool, error) {
_, err := xattr.Get(filepath.Join(bucket, path), "user.etag")
_, err := xattr.Get(filepath.Join(bucket, path), etagkey)
if isNoAttr(err) {
return false, nil
}
@@ -1019,7 +1036,7 @@ func (p *Posix) ListObjects(bucket, prefix, marker, delim string, maxkeys int) (
}
return true, nil
}, func(path string) (string, error) {
etag, err := xattr.Get(filepath.Join(bucket, path), "user.etag")
etag, err := xattr.Get(filepath.Join(bucket, path), etagkey)
return string(etag), err
}, []string{metaTmpDir})
if err != nil {
@@ -1051,7 +1068,7 @@ func (p *Posix) ListObjectsV2(bucket, prefix, marker, delim string, maxkeys int)
fileSystem := os.DirFS(bucket)
results, err := backend.Walk(fileSystem, prefix, delim, marker, maxkeys,
func(path string) (bool, error) {
_, err := xattr.Get(filepath.Join(bucket, path), "user.etag")
_, err := xattr.Get(filepath.Join(bucket, path), etagkey)
if isNoAttr(err) {
return false, nil
}
@@ -1060,7 +1077,7 @@ func (p *Posix) ListObjectsV2(bucket, prefix, marker, delim string, maxkeys int)
}
return true, nil
}, func(path string) (string, error) {
etag, err := xattr.Get(filepath.Join(bucket, path), "user.etag")
etag, err := xattr.Get(filepath.Join(bucket, path), etagkey)
return string(etag), err
}, []string{metaTmpDir})
if err != nil {
@@ -1089,7 +1106,7 @@ func (p *Posix) PutBucketAcl(bucket string, data []byte) error {
return fmt.Errorf("stat bucket: %w", err)
}
if err := xattr.Set(bucket, "user.acl", data); err != nil {
if err := xattr.Set(bucket, aclkey, data); err != nil {
return fmt.Errorf("set acl: %w", err)
}
@@ -1105,7 +1122,7 @@ func (p *Posix) GetBucketAcl(bucket string) ([]byte, error) {
return nil, fmt.Errorf("stat bucket: %w", err)
}
b, err := xattr.Get(bucket, "user.acl")
b, err := xattr.Get(bucket, aclkey)
if err != nil {
return nil, fmt.Errorf("get acl: %w", err)
}
@@ -1185,6 +1202,65 @@ func (p *Posix) RemoveTags(bucket, object string) error {
return p.SetTags(bucket, object, nil)
}
func (p *Posix) GetIAM() ([]byte, error) {
p.mu.RLock()
defer p.mu.Unlock()
if !p.iamvalid || !p.iamexpire.After(time.Now()) {
p.mu.Unlock()
err := p.refreshIAM()
p.mu.RLock()
if err != nil {
return nil, err
}
}
return p.iamcache, nil
}
func (p *Posix) refreshIAM() error {
p.mu.Lock()
defer p.mu.Unlock()
b, err := xattr.FGet(p.rootfd, iamkey)
if isNoAttr(err) {
return err
}
p.iamcache = b
p.iamvalid = true
p.iamexpire = time.Now().Add(cacheDuration)
return nil
}
func (p *Posix) StoreIAM(update auth.UpdateAcctFunc) error {
p.mu.Lock()
defer p.mu.Unlock()
b, err := xattr.FGet(p.rootfd, iamkey)
if isNoAttr(err) {
return err
}
b, err = update(b)
if err != nil {
return err
}
// TODO: use xattr.FRemove/xattr.FSetWithFlags/xattr.XATTR_CREATE
// to detect racing updates, loop on update race fail
err = xattr.FSet(p.rootfd, iamkey, b)
if err != nil {
return err
}
p.iamcache = b
p.iamvalid = true
p.iamexpire = time.Now().Add(cacheDuration)
return nil
}
func isNoAttr(err error) bool {
if err == nil {
return false
+3 -4
View File
@@ -133,7 +133,7 @@ func initFlags() []cli.Flag {
}
}
func runGateway(be backend.Backend) error {
func runGateway(be backend.Backend, s auth.Storer) error {
app := fiber.New(fiber.Config{
AppName: "versitygw",
ServerHeader: "VERSITYGW",
@@ -161,7 +161,7 @@ func runGateway(be backend.Backend) error {
opts = append(opts, s3api.WithDebug())
}
iam, err := auth.InitIAM()
iam, err := auth.NewInternal(s)
if err != nil {
return err
}
@@ -169,8 +169,7 @@ func runGateway(be backend.Backend) error {
srv, err := s3api.New(app, be, middlewares.RootUserConfig{
Access: rootUserAccess,
Secret: rootUserSecret,
Region: region,
}, port, iam, opts...)
}, port, region, iam, opts...)
if err != nil {
return fmt.Errorf("init gateway: %v", err)
}
+1 -1
View File
@@ -49,5 +49,5 @@ func runPosix(ctx *cli.Context) error {
return fmt.Errorf("init posix: %v", err)
}
return runGateway(be)
return runGateway(be, be)
}
+1 -1
View File
@@ -52,5 +52,5 @@ func runScoutfs(ctx *cli.Context) error {
return fmt.Errorf("init scoutfs: %v", err)
}
return runGateway(be)
return runGateway(be, be)
}
+3 -3
View File
@@ -26,16 +26,16 @@ type AdminController struct {
}
func (c AdminController) CreateUser(ctx *fiber.Ctx) error {
access, secret, role, region := ctx.Query("access"), ctx.Query("secret"), ctx.Query("role"), ctx.Query("region")
access, secret, role := ctx.Query("access"), ctx.Query("secret"), ctx.Query("role")
requesterRole := ctx.Locals("role")
if requesterRole != "admin" {
return fmt.Errorf("access denied: only admin users have access to this resource")
}
user := auth.Account{Secret: secret, Role: role, Region: region}
user := auth.Account{Secret: secret, Role: role}
err := c.IAMService.CreateAccount(access, &user)
err := c.IAMService.CreateAccount(access, user)
if err != nil {
return fmt.Errorf("failed to create a user: %w", err)
}
+3 -7
View File
@@ -36,7 +36,8 @@ import (
)
type S3ApiController struct {
be backend.Backend
be backend.Backend
iam auth.IAMService
}
func New(be backend.Backend) S3ApiController {
@@ -257,12 +258,7 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error {
AccessControlPolicy: &types.AccessControlPolicy{Owner: &types.Owner{ID: &access}},
}
iam, err := auth.GetIAMConfig()
if err != nil {
return SendResponse(ctx, err)
}
err = auth.UpdateACL(input, parsedAcl, *iam)
err = auth.UpdateACL(input, parsedAcl, c.iam)
return SendResponse(ctx, err)
}
+12 -13
View File
@@ -38,10 +38,9 @@ const (
type RootUserConfig struct {
Access string
Secret string
Region string
}
func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, debug bool) fiber.Handler {
func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string, debug bool) fiber.Handler {
acct := accounts{root: root, iam: iam}
return func(ctx *fiber.Ctx) error {
@@ -74,10 +73,13 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, debug bool) fib
}
signedHdrs := strings.Split(signHdrKv[1], ";")
account := acct.getAccount(creds[0])
if account == nil {
account, err := acct.getAccount(creds[0])
if err == auth.ErrNoSuchUser {
return controllers.SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
}
if err != nil {
return controllers.SendResponse(ctx, err)
}
// Check X-Amz-Date header
date := ctx.Get("X-Amz-Date")
@@ -113,7 +115,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, debug bool) fib
signErr := signer.SignHTTP(req.Context(), aws.Credentials{
AccessKeyID: creds[0],
SecretAccessKey: account.Secret,
}, req, hexPayload, creds[3], account.Region, tdate, func(options *v4.SignerOptions) {
}, req, hexPayload, creds[3], region, tdate, func(options *v4.SignerOptions) {
if debug {
options.LogSigning = true
options.Logger = logging.NewStandardLogger(os.Stderr)
@@ -147,16 +149,13 @@ type accounts struct {
iam auth.IAMService
}
func (a accounts) getAccount(access string) *auth.Account {
var account *auth.Account
func (a accounts) getAccount(access string) (auth.Account, error) {
if access == a.root.Access {
account = &auth.Account{
return auth.Account{
Secret: a.root.Secret,
Role: "admin",
Region: a.root.Region,
}
} else {
account = a.iam.GetUserAccount(access)
}, nil
}
return account
return a.iam.GetUserAccount(access)
}
+1 -1
View File
@@ -39,7 +39,7 @@ func TestS3ApiRouter_Init(t *testing.T) {
args: args{
app: fiber.New(),
be: backend.BackendUnsupported{},
iam: auth.IAMServiceUnsupported{},
iam: &auth.IAMServiceInternal{},
},
},
}
+2 -2
View File
@@ -33,7 +33,7 @@ type S3ApiServer struct {
debug bool
}
func New(app *fiber.App, be backend.Backend, root middlewares.RootUserConfig, port string, iam auth.IAMService, opts ...Option) (*S3ApiServer, error) {
func New(app *fiber.App, be backend.Backend, root middlewares.RootUserConfig, port, region string, iam auth.IAMService, opts ...Option) (*S3ApiServer, error) {
server := &S3ApiServer{
app: app,
backend: be,
@@ -45,7 +45,7 @@ func New(app *fiber.App, be backend.Backend, root middlewares.RootUserConfig, po
opt(server)
}
app.Use(middlewares.VerifyV4Signature(root, iam, server.debug))
app.Use(middlewares.VerifyV4Signature(root, iam, region, server.debug))
app.Use(logger.New())
app.Use(middlewares.VerifyMD5Body())
server.router.Init(app, be, iam)
+1 -1
View File
@@ -63,7 +63,7 @@ func TestNew(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotS3ApiServer, err := New(tt.args.app, tt.args.be, tt.args.root,
tt.args.port, auth.IAMServiceUnsupported{})
tt.args.port, "us-east-1", &auth.IAMServiceInternal{})
if (err != nil) != tt.wantErr {
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
return