aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Lebedev <lebedev_k@tochka.com>2021-04-06 13:43:08 +0500
committerKonstantin Lebedev <lebedev_k@tochka.com>2021-04-06 13:43:08 +0500
commited79baa30fe5687a35a9a61e2dcf3b4750064d36 (patch)
tree538fa799a24a9ed2cc148d9ab4bf95f3ad5d8b04
parent8a95f9c10c3d4c9e1f6761f5620da3e5253398f5 (diff)
downloadseaweedfs-ed79baa30fe5687a35a9a61e2dcf3b4750064d36.tar.xz
seaweedfs-ed79baa30fe5687a35a9a61e2dcf3b4750064d36.zip
add tests
-rw-r--r--weed/iamapi/iamapi_handlers.go1
-rw-r--r--weed/iamapi/iamapi_management_handlers.go47
-rw-r--r--weed/iamapi/iamapi_server.go48
-rw-r--r--weed/iamapi/iamapi_test.go157
4 files changed, 217 insertions, 36 deletions
diff --git a/weed/iamapi/iamapi_handlers.go b/weed/iamapi/iamapi_handlers.go
index 962717ad9..fdaf4dd69 100644
--- a/weed/iamapi/iamapi_handlers.go
+++ b/weed/iamapi/iamapi_handlers.go
@@ -55,6 +55,7 @@ func writeIamErrorResponse(w http.ResponseWriter, err error, object string, valu
errorResp := ErrorResponse{}
errorResp.Error.Type = "Sender"
errorResp.Error.Code = &errCode
+ glog.Errorf("Response %+v", err)
switch errCode {
case iam.ErrCodeNoSuchEntityException:
msg := fmt.Sprintf("The %s with name %s cannot be found.", object, value)
diff --git a/weed/iamapi/iamapi_management_handlers.go b/weed/iamapi/iamapi_management_handlers.go
index e4daa081f..470731064 100644
--- a/weed/iamapi/iamapi_management_handlers.go
+++ b/weed/iamapi/iamapi_management_handlers.go
@@ -1,14 +1,10 @@
package iamapi
import (
- "bytes"
"crypto/sha1"
"encoding/json"
"fmt"
- "github.com/chrislusf/seaweedfs/weed/filer"
"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/s3api/s3_constants"
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
@@ -16,6 +12,7 @@ import (
"net/http"
"net/url"
"strings"
+ "sync"
"time"
"github.com/aws/aws-sdk-go/service/iam"
@@ -32,13 +29,15 @@ var (
policyDocuments = map[string]*PolicyDocument{}
)
+type Statement struct {
+ Effect string `json:"Effect"`
+ Action []string `json:"Action"`
+ Resource []string `json:"Resource"`
+}
+
type PolicyDocument struct {
- Version string `json:"Version"`
- Statement []struct {
- Effect string `json:"Effect"`
- Action []string `json:"Action"`
- Resource []string `json:"Resource"`
- } `json:"Statement"`
+ Version string `json:"Version"`
+ Statement []*Statement `json:"Statement"`
}
func Hash(s *string) string {
@@ -252,13 +251,16 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
return
}
values := r.PostForm
+ var s3cfgLock sync.RWMutex
+ s3cfgLock.RLock()
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)
return
}
+ s3cfgLock.RUnlock()
- glog.Info("values ", values)
+ glog.V(4).Infof("DoActions: %+v", values)
var response interface{}
var err error
changed := true
@@ -292,12 +294,14 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
case "CreatePolicy":
response, err = iama.CreatePolicy(s3cfg, values)
if err != nil {
+ glog.Errorf("CreatePolicy: %+v", err)
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
return
}
case "PutUserPolicy":
response, err = iama.PutUserPolicy(s3cfg, values)
if err != nil {
+ glog.Errorf("PutUserPolicy: %+v", err)
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
return
}
@@ -306,22 +310,9 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
return
}
if changed {
- buf := bytes.Buffer{}
- if err := filer.S3ConfigurationToText(&buf, s3cfg); err != nil {
- glog.Error("S3ConfigurationToText: ", err)
- 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
- },
- )
+ s3cfgLock.Lock()
+ err := iama.s3ApiConfig.PutS3ApiConfiguration(s3cfg)
+ s3cfgLock.Unlock()
if err != nil {
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
return
diff --git a/weed/iamapi/iamapi_server.go b/weed/iamapi/iamapi_server.go
index 00c4a69a2..7698fab71 100644
--- a/weed/iamapi/iamapi_server.go
+++ b/weed/iamapi/iamapi_server.go
@@ -1,10 +1,10 @@
package iamapi
// https://docs.aws.amazon.com/cli/latest/reference/iam/list-roles.html
-// https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateRole.html
import (
"bytes"
+ "fmt"
"github.com/chrislusf/seaweedfs/weed/filer"
"github.com/chrislusf/seaweedfs/weed/pb"
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
@@ -16,6 +16,16 @@ import (
"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 {
Masters string
Filer string
@@ -25,17 +35,22 @@ type IamServerOption struct {
}
type IamApiServer struct {
- option *IamServerOption
- masterClient *wdclient.MasterClient
- filerclient *filer_pb.SeaweedFilerClient
+ s3ApiConfig IamS3ApiConfig
+ filerclient *filer_pb.SeaweedFilerClient
}
+var s3ApiConfigure IamS3ApiConfig
+
func NewIamApiServer(router *mux.Router, option *IamServerOption) (iamApiServer *IamApiServer, err error) {
- iamApiServer = &IamApiServer{
+ s3ApiConfigure = IamS3ApiConfigure{
option: option,
masterClient: wdclient.NewMasterClient(option.GrpcDialOption, pb.AdminShellClient, "", 0, "", strings.Split(option.Masters, ",")),
}
+ iamApiServer = &IamApiServer{
+ s3ApiConfig: s3ApiConfigure,
+ }
+
iamApiServer.registerRouter(router)
return iamApiServer, nil
@@ -52,10 +67,10 @@ func (iama *IamApiServer) registerRouter(router *mux.Router) {
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
- err = pb.WithGrpcFilerClient(iama.option.FilerGrpcAddress, iama.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
- if err = filer.ReadEntry(iama.masterClient, client, filer.IamConfigDirecotry, filer.IamIdentityFile, &buf); err != nil {
+ err = pb.WithGrpcFilerClient(iam.option.FilerGrpcAddress, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
+ if err = filer.ReadEntry(iam.masterClient, client, filer.IamConfigDirecotry, filer.IamIdentityFile, &buf); err != nil {
return err
}
return nil
@@ -70,3 +85,20 @@ func (iama *IamApiServer) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration
}
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
+ },
+ )
+}
diff --git a/weed/iamapi/iamapi_test.go b/weed/iamapi/iamapi_test.go
new file mode 100644
index 000000000..f989626e6
--- /dev/null
+++ b/weed/iamapi/iamapi_test.go
@@ -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)
+}