mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
GetUserPolicy
This commit is contained in:
parent
ba175f81b5
commit
5021bea698
1
go.mod
1
go.mod
|
@ -42,6 +42,7 @@ require (
|
|||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.11.0 // indirect
|
||||
github.com/jcmturner/gofork v1.0.0 // indirect
|
||||
github.com/jinzhu/copier v0.2.8
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/karlseguin/ccache v2.0.3+incompatible // indirect
|
||||
github.com/karlseguin/ccache/v2 v2.0.7
|
||||
|
|
2
go.sum
2
go.sum
|
@ -437,6 +437,8 @@ github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjL
|
|||
github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jinzhu/copier v0.2.8 h1:N8MbL5niMwE3P4dOwurJixz5rMkKfujmMRFmAanSzWE=
|
||||
github.com/jinzhu/copier v0.2.8/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
|
|
|
@ -18,6 +18,7 @@ const (
|
|||
FilerConfName = "filer.conf"
|
||||
IamConfigDirecotry = "/etc/iam"
|
||||
IamIdentityFile = "identity.json"
|
||||
IamPoliciesFile = "policies.json"
|
||||
)
|
||||
|
||||
type FilerConf struct {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -21,25 +22,75 @@ import (
|
|||
const (
|
||||
charsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
charset = charsetUpper + "abcdefghijklmnopqrstuvwxyz/"
|
||||
policyDocumentVersion = "2012-10-17"
|
||||
StatementActionAdmin = "*"
|
||||
StatementActionWrite = "Put*"
|
||||
StatementActionRead = "Get*"
|
||||
StatementActionList = "List*"
|
||||
StatementActionTagging = "Tagging*"
|
||||
)
|
||||
|
||||
var (
|
||||
seededRand *rand.Rand = rand.New(
|
||||
rand.NewSource(time.Now().UnixNano()))
|
||||
policyDocuments = map[string]*PolicyDocument{}
|
||||
policyLock = sync.RWMutex{}
|
||||
)
|
||||
|
||||
func MapToStatementAction(action string) string {
|
||||
switch action {
|
||||
case StatementActionAdmin:
|
||||
return s3_constants.ACTION_ADMIN
|
||||
case StatementActionWrite:
|
||||
return s3_constants.ACTION_WRITE
|
||||
case StatementActionRead:
|
||||
return s3_constants.ACTION_READ
|
||||
case StatementActionList:
|
||||
return s3_constants.ACTION_LIST
|
||||
case StatementActionTagging:
|
||||
return s3_constants.ACTION_TAGGING
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func MapToIdentitiesAction(action string) string {
|
||||
switch action {
|
||||
case s3_constants.ACTION_ADMIN:
|
||||
return StatementActionAdmin
|
||||
case s3_constants.ACTION_WRITE:
|
||||
return StatementActionWrite
|
||||
case s3_constants.ACTION_READ:
|
||||
return StatementActionRead
|
||||
case s3_constants.ACTION_LIST:
|
||||
return StatementActionList
|
||||
case s3_constants.ACTION_TAGGING:
|
||||
return StatementActionTagging
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type Statement struct {
|
||||
Effect string `json:"Effect"`
|
||||
Action []string `json:"Action"`
|
||||
Resource []string `json:"Resource"`
|
||||
}
|
||||
|
||||
type Policies struct {
|
||||
Policies map[string]PolicyDocument `json:"policies"`
|
||||
}
|
||||
|
||||
type PolicyDocument struct {
|
||||
Version string `json:"Version"`
|
||||
Statement []*Statement `json:"Statement"`
|
||||
}
|
||||
|
||||
func (p PolicyDocument) String() string {
|
||||
b, _ := json.Marshal(p)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func Hash(s *string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(*s))
|
||||
|
@ -83,7 +134,7 @@ func (iama *IamApiServer) CreateUser(s3cfg *iam_pb.S3ApiConfiguration, values ur
|
|||
func (iama *IamApiServer) DeleteUser(s3cfg *iam_pb.S3ApiConfiguration, userName string) (resp DeleteUserResponse, err error) {
|
||||
for i, ident := range s3cfg.Identities {
|
||||
if userName == ident.Name {
|
||||
ident.Credentials = append(ident.Credentials[:i], ident.Credentials[i+1:]...)
|
||||
s3cfg.Identities = append(s3cfg.Identities[:i], s3cfg.Identities[i+1:]...)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +170,16 @@ func (iama *IamApiServer) CreatePolicy(s3cfg *iam_pb.S3ApiConfiguration, values
|
|||
resp.CreatePolicyResult.Policy.PolicyName = &policyName
|
||||
resp.CreatePolicyResult.Policy.Arn = &arn
|
||||
resp.CreatePolicyResult.Policy.PolicyId = &policyId
|
||||
policyDocuments[policyName] = &policyDocument
|
||||
policies := Policies{}
|
||||
policyLock.Lock()
|
||||
defer policyLock.Unlock()
|
||||
if err = iama.s3ApiConfig.GetPolicies(&policies); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
policies.Policies[policyName] = policyDocument
|
||||
if err = iama.s3ApiConfig.PutPolicies(&policies); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -144,6 +204,60 @@ func (iama *IamApiServer) PutUserPolicy(s3cfg *iam_pb.S3ApiConfiguration, values
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (iama *IamApiServer) GetUserPolicy(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp GetUserPolicyResponse, err error) {
|
||||
userName := values.Get("UserName")
|
||||
policyName := values.Get("PolicyName")
|
||||
for _, ident := range s3cfg.Identities {
|
||||
if userName != ident.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
resp.GetUserPolicyResult.UserName = userName
|
||||
resp.GetUserPolicyResult.PolicyName = policyName
|
||||
if len(ident.Actions) == 0 {
|
||||
return resp, fmt.Errorf(iam.ErrCodeNoSuchEntityException)
|
||||
}
|
||||
|
||||
policyDocument := PolicyDocument{Version: policyDocumentVersion}
|
||||
statements := make(map[string][]string)
|
||||
for _, action := range ident.Actions {
|
||||
// parse "Read:EXAMPLE-BUCKET"
|
||||
act := strings.Split(action, ":")
|
||||
|
||||
resource := "*"
|
||||
if len(act) == 2 {
|
||||
resource = fmt.Sprintf("arn:aws:s3:::%s/*", act[1])
|
||||
}
|
||||
statements[resource] = append(statements[resource],
|
||||
fmt.Sprintf("s3:%s", MapToIdentitiesAction(act[0])),
|
||||
)
|
||||
}
|
||||
for resource, actions := range statements {
|
||||
isEqAction := false
|
||||
for i, statement := range policyDocument.Statement {
|
||||
if reflect.DeepEqual(statement.Action, actions) {
|
||||
policyDocument.Statement[i].Resource = append(
|
||||
policyDocument.Statement[i].Resource, resource)
|
||||
isEqAction = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isEqAction {
|
||||
continue
|
||||
}
|
||||
policyDocumentStatement := Statement{
|
||||
Effect: "Allow",
|
||||
Action: actions,
|
||||
}
|
||||
policyDocumentStatement.Resource = append(policyDocumentStatement.Resource, resource)
|
||||
policyDocument.Statement = append(policyDocument.Statement, &policyDocumentStatement)
|
||||
}
|
||||
resp.GetUserPolicyResult.PolicyDocument = policyDocument.String()
|
||||
return resp, nil
|
||||
}
|
||||
return resp, fmt.Errorf(iam.ErrCodeNoSuchEntityException)
|
||||
}
|
||||
|
||||
func (iama *IamApiServer) DeleteUserPolicy(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp PutUserPolicyResponse, err error) {
|
||||
userName := values.Get("UserName")
|
||||
for i, ident := range s3cfg.Identities {
|
||||
|
@ -155,21 +269,6 @@ func (iama *IamApiServer) DeleteUserPolicy(s3cfg *iam_pb.S3ApiConfiguration, val
|
|||
return resp, fmt.Errorf(iam.ErrCodeNoSuchEntityException)
|
||||
}
|
||||
|
||||
func MapAction(action string) string {
|
||||
switch action {
|
||||
case "*":
|
||||
return s3_constants.ACTION_ADMIN
|
||||
case "Put*":
|
||||
return s3_constants.ACTION_WRITE
|
||||
case "Get*":
|
||||
return s3_constants.ACTION_READ
|
||||
case "List*":
|
||||
return s3_constants.ACTION_LIST
|
||||
default:
|
||||
return s3_constants.ACTION_TAGGING
|
||||
}
|
||||
}
|
||||
|
||||
func GetActions(policy *PolicyDocument) (actions []string) {
|
||||
for _, statement := range policy.Statement {
|
||||
if statement.Effect != "Allow" {
|
||||
|
@ -189,8 +288,9 @@ func GetActions(policy *PolicyDocument) (actions []string) {
|
|||
glog.Infof("not match action: %s", act)
|
||||
continue
|
||||
}
|
||||
statementAction := MapToStatementAction(act[1])
|
||||
if res[5] == "*" {
|
||||
actions = append(actions, MapAction(act[1]))
|
||||
actions = append(actions, statementAction)
|
||||
continue
|
||||
}
|
||||
// Parse my-bucket/shared/*
|
||||
|
@ -199,7 +299,7 @@ func GetActions(policy *PolicyDocument) (actions []string) {
|
|||
glog.Infof("not match bucket: %s", path)
|
||||
continue
|
||||
}
|
||||
actions = append(actions, fmt.Sprintf("%s:%s", MapAction(act[1]), path[0]))
|
||||
actions = append(actions, fmt.Sprintf("%s:%s", statementAction, path[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,6 +391,7 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
|||
writeIamErrorResponse(w, err, "user", userName, nil)
|
||||
return
|
||||
}
|
||||
changed = false
|
||||
case "DeleteUser":
|
||||
userName := values.Get("UserName")
|
||||
response, err = iama.DeleteUser(s3cfg, userName)
|
||||
|
@ -316,6 +417,13 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
|||
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
|
||||
return
|
||||
}
|
||||
case "GetUserPolicy":
|
||||
response, err = iama.GetUserPolicy(s3cfg, values)
|
||||
if err != nil {
|
||||
writeIamErrorResponse(w, err, "user", values.Get("UserName"), nil)
|
||||
return
|
||||
}
|
||||
changed = false
|
||||
case "DeleteUserPolicy":
|
||||
if response, err = iama.DeleteUserPolicy(s3cfg, values); err != nil {
|
||||
writeIamErrorResponse(w, err, "user", values.Get("UserName"), nil)
|
||||
|
|
|
@ -79,6 +79,16 @@ type PutUserPolicyResponse struct {
|
|||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ PutUserPolicyResponse"`
|
||||
}
|
||||
|
||||
type GetUserPolicyResponse struct {
|
||||
CommonResponse
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ GetUserPolicyResponse"`
|
||||
GetUserPolicyResult struct {
|
||||
UserName string `xml:"UserName"`
|
||||
PolicyName string `xml:"PolicyName"`
|
||||
PolicyDocument string `xml:"PolicyDocument"`
|
||||
} `xml:"GetUserPolicyResult"`
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
CommonResponse
|
||||
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ErrorResponse"`
|
||||
|
|
|
@ -4,6 +4,7 @@ package iamapi
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb"
|
||||
|
@ -21,6 +22,8 @@ import (
|
|||
type IamS3ApiConfig interface {
|
||||
GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error)
|
||||
PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error)
|
||||
GetPolicies(policies *Policies) (err error)
|
||||
PutPolicies(policies *Policies) (err error)
|
||||
}
|
||||
|
||||
type IamS3ApiConfigure struct {
|
||||
|
@ -106,3 +109,41 @@ func (iam IamS3ApiConfigure) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfigurat
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (iam IamS3ApiConfigure) GetPolicies(policies *Policies) (err error) {
|
||||
var buf bytes.Buffer
|
||||
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.IamPoliciesFile, &buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if buf.Len() == 0 {
|
||||
policies.Policies = make(map[string]PolicyDocument)
|
||||
return nil
|
||||
}
|
||||
if err := json.Unmarshal(buf.Bytes(), policies); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iam IamS3ApiConfigure) PutPolicies(policies *Policies) (err error) {
|
||||
var b []byte
|
||||
if b, err = json.Marshal(policies); err != nil {
|
||||
return err
|
||||
}
|
||||
return pb.WithGrpcFilerClient(
|
||||
iam.option.FilerGrpcAddress,
|
||||
iam.option.GrpcDialOption,
|
||||
func(client filer_pb.SeaweedFilerClient) error {
|
||||
if err := filer.SaveInsideFiler(client, filer.IamConfigDirecotry, filer.IamPoliciesFile, b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,29 +7,43 @@ import (
|
|||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jinzhu/copier"
|
||||
"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)
|
||||
var GetPolicies func(policies *Policies) (err error)
|
||||
var PutPolicies func(policies *Policies) (err error)
|
||||
|
||||
var s3config = iam_pb.S3ApiConfiguration{}
|
||||
var policiesFile = Policies{Policies: make(map[string]PolicyDocument)}
|
||||
var ias = IamApiServer{s3ApiConfig: iamS3ApiConfigureMock{}}
|
||||
|
||||
type iamS3ApiConfigureMock struct{}
|
||||
|
||||
func (iam iamS3ApiConfigureMock) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
s3cfg = &S3config
|
||||
_ = copier.Copy(&s3cfg.Identities, &s3config.Identities)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iam iamS3ApiConfigureMock) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
S3config = *s3cfg
|
||||
_ = copier.Copy(&s3config.Identities, &s3cfg.Identities)
|
||||
return nil
|
||||
}
|
||||
|
||||
var a = IamApiServer{}
|
||||
func (iam iamS3ApiConfigureMock) GetPolicies(policies *Policies) (err error) {
|
||||
_ = copier.Copy(&policies, &policiesFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iam iamS3ApiConfigureMock) PutPolicies(policies *Policies) (err error) {
|
||||
_ = copier.Copy(&policiesFile, &policies)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
userName := aws.String("Test")
|
||||
|
@ -64,17 +78,6 @@ func TestListAccessKeys(t *testing.T) {
|
|||
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}
|
||||
|
@ -83,7 +86,7 @@ func TestGetUser(t *testing.T) {
|
|||
out := GetUserResponse{}
|
||||
response, err := executeRequest(req.HTTPRequest, out)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, http.StatusNotFound, response.Code)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
}
|
||||
|
||||
// Todo flat statement
|
||||
|
@ -147,11 +150,32 @@ func TestPutUserPolicy(t *testing.T) {
|
|||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
}
|
||||
|
||||
func TestGetUserPolicy(t *testing.T) {
|
||||
userName := aws.String("Test")
|
||||
params := &iam.GetUserPolicyInput{UserName: userName, PolicyName: aws.String("S3-read-only-example-bucket")}
|
||||
req, _ := iam.New(session.New()).GetUserPolicyRequest(params)
|
||||
_ = req.Build()
|
||||
out := GetUserPolicyResponse{}
|
||||
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.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.Path("/").Methods("POST").HandlerFunc(ias.DoActions)
|
||||
apiRouter.ServeHTTP(rr, req)
|
||||
return rr, xml.Unmarshal(rr.Body.Bytes(), &v)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue