aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--weed/iamapi/iamapi_management_handlers.go143
1 files changed, 139 insertions, 4 deletions
diff --git a/weed/iamapi/iamapi_management_handlers.go b/weed/iamapi/iamapi_management_handlers.go
index 2347c9a80..22bc8748a 100644
--- a/weed/iamapi/iamapi_management_handlers.go
+++ b/weed/iamapi/iamapi_management_handlers.go
@@ -1,14 +1,18 @@
package iamapi
import (
+ "crypto/sha1"
+ "encoding/json"
"encoding/xml"
"fmt"
"github.com/chrislusf/seaweedfs/weed/glog"
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
+ "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
"math/rand"
"net/http"
"net/url"
+ "strings"
"time"
// "github.com/aws/aws-sdk-go/aws"
@@ -21,11 +25,19 @@ const (
charset = charsetUpper + "abcdefghijklmnopqrstuvwxyz/"
)
-var seededRand *rand.Rand = rand.New(
- rand.NewSource(time.Now().UnixNano()))
+var (
+ seededRand *rand.Rand = rand.New(
+ rand.NewSource(time.Now().UnixNano()))
+ policyDocuments = map[string]*PolicyDocument{}
+)
-type Response interface {
- SetRequestId()
+type PolicyDocument struct {
+ Version string `json:"Version"`
+ Statement []struct {
+ Effect string `json:"Effect"`
+ Action []string `json:"Action"`
+ Resource []string `json:"Resource"`
+ } `json:"Statement"`
}
type CommonResponse struct {
@@ -62,6 +74,14 @@ type DeleteAccessKeyResponse struct {
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteAccessKeyResponse"`
}
+type CreatePolicyResponse struct {
+ CommonResponse
+ XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreatePolicyResponse"`
+ CreatePolicyResult struct {
+ Policy iam.Policy `xml:"Policy"`
+ } `xml:"CreatePolicyResult"`
+}
+
type CreateUserResponse struct {
CommonResponse
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateUserResponse"`
@@ -78,10 +98,21 @@ type CreateAccessKeyResponse struct {
} `xml:"CreateAccessKeyResult"`
}
+type PutUserPolicyResponse struct {
+ CommonResponse
+ XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ PutUserPolicyResponse"`
+}
+
func (r *CommonResponse) SetRequestId() {
r.ResponseMetadata.RequestId = fmt.Sprintf("%d", time.Now().UnixNano())
}
+func Hash(s *string) string {
+ h := sha1.New()
+ h.Write([]byte(*s))
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
func StringWithCharset(length int, charset string) string {
b := make([]byte, length)
for i := range b {
@@ -115,6 +146,96 @@ func (iama *IamApiServer) CreateUser(s3cfg *iam_pb.S3ApiConfiguration, values ur
s3cfg.Identities = append(s3cfg.Identities, &iam_pb.Identity{Name: userName})
return resp
}
+func GetPolicyDocument(policy *string) (policyDocument PolicyDocument, err error) {
+ if err = json.Unmarshal([]byte(*policy), &policyDocument); err != nil {
+ return PolicyDocument{}, err
+ }
+ return policyDocument, err
+}
+
+func (iama *IamApiServer) CreatePolicy(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp CreatePolicyResponse, err error) {
+ policyName := values.Get("PolicyName")
+ policyDocumentString := values.Get("PolicyDocument")
+ policyDocument, err := GetPolicyDocument(&policyDocumentString)
+ if err != nil {
+ return CreatePolicyResponse{}, err
+ }
+ policyId := Hash(&policyDocumentString)
+ arn := fmt.Sprintf("arn:aws:iam:::policy/%s", policyName)
+ resp.CreatePolicyResult.Policy.PolicyName = &policyName
+ resp.CreatePolicyResult.Policy.Arn = &arn
+ resp.CreatePolicyResult.Policy.PolicyId = &policyId
+ policyDocuments[policyName] = &policyDocument
+ return resp, nil
+}
+
+func (iama *IamApiServer) PutUserPolicy(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp PutUserPolicyResponse, err error) {
+ userName := values.Get("UserName")
+ policyName := values.Get("PolicyName")
+ policyDocumentString := values.Get("PolicyDocument")
+ policyDocument, err := GetPolicyDocument(&policyDocumentString)
+ if err != nil {
+ return PutUserPolicyResponse{}, err
+ }
+ policyDocuments[policyName] = &policyDocument
+ actions := GetActions(&policyDocument)
+ for _, ident := range s3cfg.Identities {
+ if userName == ident.Name {
+ for _, action := range actions {
+ ident.Actions = append(ident.Actions, action)
+ }
+ break
+ }
+ }
+ return resp, nil
+}
+
+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" {
+ continue
+ }
+ for _, resource := range statement.Resource {
+ // Parse "arn:aws:s3:::my-bucket/shared/*"
+ res := strings.Split(resource, ":")
+ if len(res) != 6 || res[0] != "arn:" || res[1] != "aws" || res[2] != "s3" {
+ continue
+ }
+ for _, action := range statement.Action {
+ // Parse "s3:Get*"
+ act := strings.Split(action, ":")
+ if len(act) != 2 || act[0] != "s3" {
+ continue
+ }
+ if res[5] == "*" {
+ actions = append(actions, MapAction(act[1]))
+ continue
+ }
+ // Parse my-bucket/shared/*
+ path := strings.Split(res[5], "/")
+ if len(path) != 2 || path[1] != "*" {
+ actions = append(actions, fmt.Sprintf("%s:%s", MapAction(act[1]), path[0]))
+ }
+ }
+ }
+ }
+ return actions
+}
func (iama *IamApiServer) DeleteUser(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp DeleteUserResponse) {
userName := values.Get("UserName")
@@ -204,6 +325,20 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
response = iama.CreateAccessKey(s3cfg, values)
case "DeleteAccessKey":
response = iama.DeleteAccessKey(s3cfg, values)
+ case "CreatePolicy":
+ var err error
+ response, err = iama.CreatePolicy(s3cfg, values)
+ if err != nil {
+ writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
+ return
+ }
+ case "PutUserPolicy":
+ var err error
+ response, err = iama.PutUserPolicy(s3cfg, values)
+ if err != nil {
+ writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
+ return
+ }
default:
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
return