seaweedfs/weed/iamapi/iamapi_management_handlers.go

323 lines
9.2 KiB
Go
Raw Normal View History

2020-12-09 20:11:49 +08:00
package iamapi
import (
"crypto/sha1"
"encoding/json"
2020-12-10 03:03:14 +08:00
"fmt"
2020-12-09 20:11:49 +08:00
"github.com/chrislusf/seaweedfs/weed/glog"
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
2020-12-09 20:11:49 +08:00
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
2020-12-10 03:03:14 +08:00
"math/rand"
2020-12-09 20:11:49 +08:00
"net/http"
"net/url"
"strings"
2021-04-06 16:43:08 +08:00
"sync"
2020-12-10 03:03:14 +08:00
"time"
2020-12-09 20:11:49 +08:00
"github.com/aws/aws-sdk-go/service/iam"
)
const (
2020-12-10 03:03:14 +08:00
charsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
charset = charsetUpper + "abcdefghijklmnopqrstuvwxyz/"
2020-12-09 20:11:49 +08:00
)
var (
seededRand *rand.Rand = rand.New(
rand.NewSource(time.Now().UnixNano()))
policyDocuments = map[string]*PolicyDocument{}
)
2020-12-10 03:03:14 +08:00
2021-04-06 16:43:08 +08:00
type Statement struct {
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource []string `json:"Resource"`
}
type PolicyDocument struct {
2021-04-06 16:43:08 +08:00
Version string `json:"Version"`
Statement []*Statement `json:"Statement"`
2020-12-10 03:03:14 +08:00
}
func Hash(s *string) string {
h := sha1.New()
h.Write([]byte(*s))
return fmt.Sprintf("%x", h.Sum(nil))
}
2020-12-10 03:03:14 +08:00
func StringWithCharset(length int, charset string) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
func (iama *IamApiServer) ListUsers(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp ListUsersResponse) {
2020-12-09 20:11:49 +08:00
for _, ident := range s3cfg.Identities {
resp.ListUsersResult.Users = append(resp.ListUsersResult.Users, &iam.User{UserName: &ident.Name})
}
return resp
}
2020-12-10 03:03:14 +08:00
func (iama *IamApiServer) ListAccessKeys(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp ListAccessKeysResponse) {
status := iam.StatusTypeActive
for _, ident := range s3cfg.Identities {
for _, cred := range ident.Credentials {
resp.ListAccessKeysResult.AccessKeyMetadata = append(resp.ListAccessKeysResult.AccessKeyMetadata,
&iam.AccessKeyMetadata{UserName: &ident.Name, AccessKeyId: &cred.AccessKey, Status: &status},
)
}
}
return resp
}
func (iama *IamApiServer) CreateUser(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp CreateUserResponse) {
userName := values.Get("UserName")
resp.CreateUserResult.User.UserName = &userName
s3cfg.Identities = append(s3cfg.Identities, &iam_pb.Identity{Name: userName})
return resp
}
2021-03-29 15:01:44 +08:00
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:]...)
return resp, nil
}
}
return resp, fmt.Errorf(iam.ErrCodeNoSuchEntityException)
}
func (iama *IamApiServer) GetUser(s3cfg *iam_pb.S3ApiConfiguration, userName string) (resp GetUserResponse, err error) {
for _, ident := range s3cfg.Identities {
if userName == ident.Name {
resp.GetUserResult.User = iam.User{UserName: &ident.Name}
return resp, nil
}
}
return resp, fmt.Errorf(iam.ErrCodeNoSuchEntityException)
}
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, ":")
2020-12-10 20:03:55 +08:00
if len(res) != 6 || res[0] != "arn" || res[1] != "aws" || res[2] != "s3" {
2020-12-11 00:03:25 +08:00
glog.Infof("not match resource: %s", res)
continue
}
for _, action := range statement.Action {
// Parse "s3:Get*"
act := strings.Split(action, ":")
if len(act) != 2 || act[0] != "s3" {
2020-12-10 20:03:55 +08:00
glog.Infof("not match action: %s", act)
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] != "*" {
2020-12-10 20:03:55 +08:00
glog.Infof("not match bucket: %s", path)
continue
}
2020-12-10 20:03:55 +08:00
actions = append(actions, fmt.Sprintf("%s:%s", MapAction(act[1]), path[0]))
}
}
}
return actions
}
2020-12-10 03:03:14 +08:00
func (iama *IamApiServer) CreateAccessKey(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp CreateAccessKeyResponse) {
userName := values.Get("UserName")
status := iam.StatusTypeActive
accessKeyId := StringWithCharset(21, charsetUpper)
secretAccessKey := StringWithCharset(42, charset)
resp.CreateAccessKeyResult.AccessKey.AccessKeyId = &accessKeyId
resp.CreateAccessKeyResult.AccessKey.SecretAccessKey = &secretAccessKey
resp.CreateAccessKeyResult.AccessKey.UserName = &userName
resp.CreateAccessKeyResult.AccessKey.Status = &status
changed := false
for _, ident := range s3cfg.Identities {
if userName == ident.Name {
ident.Credentials = append(ident.Credentials,
&iam_pb.Credential{AccessKey: accessKeyId, SecretKey: secretAccessKey})
changed = true
break
}
}
if !changed {
s3cfg.Identities = append(s3cfg.Identities,
&iam_pb.Identity{Name: userName,
Credentials: []*iam_pb.Credential{
{
AccessKey: accessKeyId,
SecretKey: secretAccessKey,
},
},
},
)
}
return resp
}
func (iama *IamApiServer) DeleteAccessKey(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp DeleteAccessKeyResponse) {
userName := values.Get("UserName")
accessKeyId := values.Get("AccessKeyId")
for _, ident := range s3cfg.Identities {
if userName == ident.Name {
for i, cred := range ident.Credentials {
if cred.AccessKey == accessKeyId {
ident.Credentials = append(ident.Credentials[:i], ident.Credentials[i+1:]...)
break
}
}
break
}
}
return resp
2020-12-09 20:11:49 +08:00
}
func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
return
}
values := r.PostForm
2021-04-06 16:43:08 +08:00
var s3cfgLock sync.RWMutex
s3cfgLock.RLock()
2020-12-09 20:11:49 +08:00
s3cfg := &iam_pb.S3ApiConfiguration{}
2021-04-06 16:43:08 +08:00
if err := iama.s3ApiConfig.GetS3ApiConfiguration(s3cfg); err != nil {
2020-12-09 20:11:49 +08:00
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
return
}
2021-04-06 16:43:08 +08:00
s3cfgLock.RUnlock()
2020-12-09 20:11:49 +08:00
2021-04-06 16:43:08 +08:00
glog.V(4).Infof("DoActions: %+v", values)
2020-12-09 20:11:49 +08:00
var response interface{}
2021-03-29 15:01:44 +08:00
var err error
2020-12-10 20:03:55 +08:00
changed := true
2020-12-09 20:11:49 +08:00
switch r.Form.Get("Action") {
case "ListUsers":
response = iama.ListUsers(s3cfg, values)
2020-12-10 20:03:55 +08:00
changed = false
2020-12-09 20:11:49 +08:00
case "ListAccessKeys":
2020-12-10 03:03:14 +08:00
response = iama.ListAccessKeys(s3cfg, values)
2020-12-10 20:03:55 +08:00
changed = false
2020-12-10 03:03:14 +08:00
case "CreateUser":
response = iama.CreateUser(s3cfg, values)
2021-03-29 15:01:44 +08:00
case "GetUser":
userName := values.Get("UserName")
response, err = iama.GetUser(s3cfg, userName)
if err != nil {
writeIamErrorResponse(w, err, "user", userName)
return
}
2020-12-10 03:03:14 +08:00
case "DeleteUser":
2021-03-29 15:01:44 +08:00
userName := values.Get("UserName")
response, err = iama.DeleteUser(s3cfg, userName)
if err != nil {
writeIamErrorResponse(w, err, "user", userName)
return
}
2020-12-10 03:03:14 +08:00
case "CreateAccessKey":
response = iama.CreateAccessKey(s3cfg, values)
case "DeleteAccessKey":
response = iama.DeleteAccessKey(s3cfg, values)
case "CreatePolicy":
response, err = iama.CreatePolicy(s3cfg, values)
if err != nil {
2021-04-06 16:43:08 +08:00
glog.Errorf("CreatePolicy: %+v", err)
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
return
}
case "PutUserPolicy":
response, err = iama.PutUserPolicy(s3cfg, values)
if err != nil {
2021-04-06 16:43:08 +08:00
glog.Errorf("PutUserPolicy: %+v", err)
writeErrorResponse(w, s3err.ErrInvalidRequest, r.URL)
return
}
2020-12-09 20:11:49 +08:00
default:
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
return
}
2020-12-10 20:03:55 +08:00
if changed {
2021-04-06 16:43:08 +08:00
s3cfgLock.Lock()
err := iama.s3ApiConfig.PutS3ApiConfiguration(s3cfg)
s3cfgLock.Unlock()
2020-12-11 00:03:25 +08:00
if err != nil {
2020-12-10 20:03:55 +08:00
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
return
}
}
2020-12-09 20:11:49 +08:00
writeSuccessResponseXML(w, encodeResponse(response))
}