|
|
@ -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 |
|
|
|