add tests

This commit is contained in:
Konstantin Lebedev 2021-04-06 13:43:08 +05:00
parent 8a95f9c10c
commit ed79baa30f
4 changed files with 217 additions and 36 deletions

View file

@ -55,6 +55,7 @@ func writeIamErrorResponse(w http.ResponseWriter, err error, object string, valu
errorResp := ErrorResponse{} errorResp := ErrorResponse{}
errorResp.Error.Type = "Sender" errorResp.Error.Type = "Sender"
errorResp.Error.Code = &errCode errorResp.Error.Code = &errCode
glog.Errorf("Response %+v", err)
switch errCode { switch errCode {
case iam.ErrCodeNoSuchEntityException: case iam.ErrCodeNoSuchEntityException:
msg := fmt.Sprintf("The %s with name %s cannot be found.", object, value) msg := fmt.Sprintf("The %s with name %s cannot be found.", object, value)

View file

@ -1,14 +1,10 @@
package iamapi package iamapi
import ( import (
"bytes"
"crypto/sha1" "crypto/sha1"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/chrislusf/seaweedfs/weed/filer"
"github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/glog"
"github.com/chrislusf/seaweedfs/weed/pb"
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb" "github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants" "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
"github.com/chrislusf/seaweedfs/weed/s3api/s3err" "github.com/chrislusf/seaweedfs/weed/s3api/s3err"
@ -16,6 +12,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"sync"
"time" "time"
"github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/iam"
@ -32,13 +29,15 @@ var (
policyDocuments = map[string]*PolicyDocument{} policyDocuments = map[string]*PolicyDocument{}
) )
type PolicyDocument struct { type Statement struct {
Version string `json:"Version"`
Statement []struct {
Effect string `json:"Effect"` Effect string `json:"Effect"`
Action []string `json:"Action"` Action []string `json:"Action"`
Resource []string `json:"Resource"` Resource []string `json:"Resource"`
} `json:"Statement"` }
type PolicyDocument struct {
Version string `json:"Version"`
Statement []*Statement `json:"Statement"`
} }
func Hash(s *string) string { func Hash(s *string) string {
@ -252,13 +251,16 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
return return
} }
values := r.PostForm values := r.PostForm
var s3cfgLock sync.RWMutex
s3cfgLock.RLock()
s3cfg := &iam_pb.S3ApiConfiguration{} s3cfg := &iam_pb.S3ApiConfiguration{}
if err := iama.GetS3ApiConfiguration(s3cfg); err != nil { if err := iama.s3ApiConfig.GetS3ApiConfiguration(s3cfg); err != nil {
writeErrorResponse(w, s3err.ErrInternalError, r.URL) writeErrorResponse(w, s3err.ErrInternalError, r.URL)
return return
} }
s3cfgLock.RUnlock()
glog.Info("values ", values) glog.V(4).Infof("DoActions: %+v", values)
var response interface{} var response interface{}
var err error var err error
changed := true changed := true
@ -292,12 +294,14 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
case "CreatePolicy": case "CreatePolicy":
response, err = iama.CreatePolicy(s3cfg, values) response, err = iama.CreatePolicy(s3cfg, values)
if err != nil { if err != nil {
glog.Errorf("CreatePolicy: %+v", err)
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL) writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
return return
} }
case "PutUserPolicy": case "PutUserPolicy":
response, err = iama.PutUserPolicy(s3cfg, values) response, err = iama.PutUserPolicy(s3cfg, values)
if err != nil { if err != nil {
glog.Errorf("PutUserPolicy: %+v", err)
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL) writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
return return
} }
@ -306,22 +310,9 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
return return
} }
if changed { if changed {
buf := bytes.Buffer{} s3cfgLock.Lock()
if err := filer.S3ConfigurationToText(&buf, s3cfg); err != nil { err := iama.s3ApiConfig.PutS3ApiConfiguration(s3cfg)
glog.Error("S3ConfigurationToText: ", err) s3cfgLock.Unlock()
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
return
}
err := pb.WithGrpcFilerClient(
iama.option.FilerGrpcAddress,
iama.option.GrpcDialOption,
func(client filer_pb.SeaweedFilerClient) error {
if err := filer.SaveInsideFiler(client, filer.IamConfigDirecotry, filer.IamIdentityFile, buf.Bytes()); err != nil {
return err
}
return nil
},
)
if err != nil { if err != nil {
writeErrorResponse(w, s3err.ErrInternalError, r.URL) writeErrorResponse(w, s3err.ErrInternalError, r.URL)
return return

View file

@ -1,10 +1,10 @@
package iamapi package iamapi
// https://docs.aws.amazon.com/cli/latest/reference/iam/list-roles.html // https://docs.aws.amazon.com/cli/latest/reference/iam/list-roles.html
// https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateRole.html
import ( import (
"bytes" "bytes"
"fmt"
"github.com/chrislusf/seaweedfs/weed/filer" "github.com/chrislusf/seaweedfs/weed/filer"
"github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb"
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb" "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
@ -16,6 +16,16 @@ import (
"strings" "strings"
) )
type IamS3ApiConfig interface {
GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error)
PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error)
}
type IamS3ApiConfigure struct {
option *IamServerOption
masterClient *wdclient.MasterClient
}
type IamServerOption struct { type IamServerOption struct {
Masters string Masters string
Filer string Filer string
@ -25,17 +35,22 @@ type IamServerOption struct {
} }
type IamApiServer struct { type IamApiServer struct {
option *IamServerOption s3ApiConfig IamS3ApiConfig
masterClient *wdclient.MasterClient
filerclient *filer_pb.SeaweedFilerClient filerclient *filer_pb.SeaweedFilerClient
} }
var s3ApiConfigure IamS3ApiConfig
func NewIamApiServer(router *mux.Router, option *IamServerOption) (iamApiServer *IamApiServer, err error) { func NewIamApiServer(router *mux.Router, option *IamServerOption) (iamApiServer *IamApiServer, err error) {
iamApiServer = &IamApiServer{ s3ApiConfigure = IamS3ApiConfigure{
option: option, option: option,
masterClient: wdclient.NewMasterClient(option.GrpcDialOption, pb.AdminShellClient, "", 0, "", strings.Split(option.Masters, ",")), masterClient: wdclient.NewMasterClient(option.GrpcDialOption, pb.AdminShellClient, "", 0, "", strings.Split(option.Masters, ",")),
} }
iamApiServer = &IamApiServer{
s3ApiConfig: s3ApiConfigure,
}
iamApiServer.registerRouter(router) iamApiServer.registerRouter(router)
return iamApiServer, nil return iamApiServer, nil
@ -52,10 +67,10 @@ func (iama *IamApiServer) registerRouter(router *mux.Router) {
apiRouter.NotFoundHandler = http.HandlerFunc(notFoundHandler) apiRouter.NotFoundHandler = http.HandlerFunc(notFoundHandler)
} }
func (iama *IamApiServer) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) { func (iam IamS3ApiConfigure) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
var buf bytes.Buffer var buf bytes.Buffer
err = pb.WithGrpcFilerClient(iama.option.FilerGrpcAddress, iama.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error { err = pb.WithGrpcFilerClient(iam.option.FilerGrpcAddress, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
if err = filer.ReadEntry(iama.masterClient, client, filer.IamConfigDirecotry, filer.IamIdentityFile, &buf); err != nil { if err = filer.ReadEntry(iam.masterClient, client, filer.IamConfigDirecotry, filer.IamIdentityFile, &buf); err != nil {
return err return err
} }
return nil return nil
@ -70,3 +85,20 @@ func (iama *IamApiServer) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration
} }
return nil return nil
} }
func (iam IamS3ApiConfigure) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
buf := bytes.Buffer{}
if err := filer.S3ConfigurationToText(&buf, s3cfg); err != nil {
return fmt.Errorf("S3ConfigurationToText: %s", err)
}
return pb.WithGrpcFilerClient(
iam.option.FilerGrpcAddress,
iam.option.GrpcDialOption,
func(client filer_pb.SeaweedFilerClient) error {
if err := filer.SaveInsideFiler(client, filer.IamConfigDirecotry, filer.IamIdentityFile, buf.Bytes()); err != nil {
return err
}
return nil
},
)
}

157
weed/iamapi/iamapi_test.go Normal file
View file

@ -0,0 +1,157 @@
package iamapi
import (
"encoding/xml"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
var S3config iam_pb.S3ApiConfiguration
var GetS3ApiConfiguration func(s3cfg *iam_pb.S3ApiConfiguration) (err error)
var PutS3ApiConfiguration func(s3cfg *iam_pb.S3ApiConfiguration) (err error)
type iamS3ApiConfigureMock struct{}
func (iam iamS3ApiConfigureMock) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
s3cfg = &S3config
return nil
}
func (iam iamS3ApiConfigureMock) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
S3config = *s3cfg
return nil
}
var a = IamApiServer{}
func TestCreateUser(t *testing.T) {
userName := aws.String("Test")
params := &iam.CreateUserInput{UserName: userName}
req, _ := iam.New(session.New()).CreateUserRequest(params)
_ = req.Build()
out := CreateUserResponse{}
response, err := executeRequest(req.HTTPRequest, out)
assert.Equal(t, nil, err)
assert.Equal(t, http.StatusOK, response.Code)
//assert.Equal(t, out.XMLName, "lol")
}
func TestListUsers(t *testing.T) {
params := &iam.ListUsersInput{}
req, _ := iam.New(session.New()).ListUsersRequest(params)
_ = req.Build()
out := ListUsersResponse{}
response, err := executeRequest(req.HTTPRequest, out)
assert.Equal(t, nil, err)
assert.Equal(t, http.StatusOK, response.Code)
}
func TestListAccessKeys(t *testing.T) {
svc := iam.New(session.New())
params := &iam.ListAccessKeysInput{}
req, _ := svc.ListAccessKeysRequest(params)
_ = req.Build()
out := ListAccessKeysResponse{}
response, err := executeRequest(req.HTTPRequest, out)
assert.Equal(t, nil, err)
assert.Equal(t, http.StatusOK, response.Code)
}
func TestDeleteUser(t *testing.T) {
userName := aws.String("Test")
params := &iam.DeleteUserInput{UserName: userName}
req, _ := iam.New(session.New()).DeleteUserRequest(params)
_ = req.Build()
out := DeleteUserResponse{}
response, err := executeRequest(req.HTTPRequest, out)
assert.Equal(t, nil, err)
assert.Equal(t, http.StatusNotFound, response.Code)
}
func TestGetUser(t *testing.T) {
userName := aws.String("Test")
params := &iam.GetUserInput{UserName: userName}
req, _ := iam.New(session.New()).GetUserRequest(params)
_ = req.Build()
out := GetUserResponse{}
response, err := executeRequest(req.HTTPRequest, out)
assert.Equal(t, nil, err)
assert.Equal(t, http.StatusNotFound, response.Code)
}
// Todo flat statement
func TestCreatePolicy(t *testing.T) {
params := &iam.CreatePolicyInput{
PolicyName: aws.String("S3-read-only-example-bucket"),
PolicyDocument: aws.String(`
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::EXAMPLE-BUCKET",
"arn:aws:s3:::EXAMPLE-BUCKET/*"
]
}
]
}`),
}
req, _ := iam.New(session.New()).CreatePolicyRequest(params)
_ = req.Build()
out := CreatePolicyResponse{}
response, err := executeRequest(req.HTTPRequest, out)
assert.Equal(t, nil, err)
assert.Equal(t, http.StatusOK, response.Code)
}
func TestPutUserPolicy(t *testing.T) {
userName := aws.String("Test")
params := &iam.PutUserPolicyInput{
UserName: userName,
PolicyName: aws.String("S3-read-only-example-bucket"),
PolicyDocument: aws.String(
`{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::EXAMPLE-BUCKET",
"arn:aws:s3:::EXAMPLE-BUCKET/*"
]
}
]
}`),
}
req, _ := iam.New(session.New()).PutUserPolicyRequest(params)
_ = req.Build()
out := PutUserPolicyResponse{}
response, err := executeRequest(req.HTTPRequest, out)
assert.Equal(t, nil, err)
assert.Equal(t, http.StatusOK, response.Code)
}
func executeRequest(req *http.Request, v interface{}) (*httptest.ResponseRecorder, error) {
rr := httptest.NewRecorder()
apiRouter := mux.NewRouter().SkipClean(true)
a.s3ApiConfig = iamS3ApiConfigureMock{}
apiRouter.Path("/").Methods("POST").HandlerFunc(a.DoActions)
apiRouter.ServeHTTP(rr, req)
return rr, xml.Unmarshal(rr.Body.Bytes(), &v)
}