package iamapi import ( "encoding/xml" "fmt" "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/pb/iam_pb" "github.com/chrislusf/seaweedfs/weed/s3api/s3err" "math/rand" "net/http" "net/url" "time" // "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" ) const ( version = "2010-05-08" charsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" charset = charsetUpper + "abcdefghijklmnopqrstuvwxyz/" ) var seededRand *rand.Rand = rand.New( rand.NewSource(time.Now().UnixNano())) type Response interface { SetRequestId() } type CommonResponse struct { ResponseMetadata struct { RequestId string `xml:"RequestId"` } `xml:"ResponseMetadata"` } type ListUsersResponse struct { CommonResponse XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListUsersResponse"` ListUsersResult struct { Users []*iam.User `xml:"Users>member"` IsTruncated bool `xml:"IsTruncated"` } `xml:"ListUsersResult"` } type ListAccessKeysResponse struct { CommonResponse XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListAccessKeysResponse"` ListAccessKeysResult struct { AccessKeyMetadata []*iam.AccessKeyMetadata `xml:"AccessKeyMetadata>member"` IsTruncated bool `xml:"IsTruncated"` } `xml:"ListAccessKeysResult"` } type DeleteUserResponse struct { CommonResponse XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteUserResponse"` } type DeleteAccessKeyResponse struct { CommonResponse XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ DeleteAccessKeyResponse"` } type CreateUserResponse struct { CommonResponse XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateUserResponse"` CreateUserResult struct { User iam.User `xml:"User"` } `xml:"CreateUserResult"` } type CreateAccessKeyResponse struct { CommonResponse XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ CreateAccessKeyResponse"` CreateAccessKeyResult struct { AccessKey iam.AccessKey `xml:"AccessKey"` } `xml:"CreateAccessKeyResult"` } func (r *CommonResponse) SetRequestId() { r.ResponseMetadata.RequestId = fmt.Sprintf("%d", time.Now().UnixNano()) } 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) { for _, ident := range s3cfg.Identities { resp.ListUsersResult.Users = append(resp.ListUsersResult.Users, &iam.User{UserName: &ident.Name}) } return resp } 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 } func (iama *IamApiServer) DeleteUser(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) (resp DeleteUserResponse) { userName := values.Get("UserName") for i, ident := range s3cfg.Identities { if userName == ident.Name { ident.Credentials = append(ident.Credentials[:i], ident.Credentials[i+1:]...) break } } return resp } 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 } 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 s3cfg := &iam_pb.S3ApiConfiguration{} if err := iama.GetS3ApiConfiguration(s3cfg); err != nil { writeErrorResponse(w, s3err.ErrInternalError, r.URL) return } glog.Info("values ", values) var response interface{} switch r.Form.Get("Action") { case "ListUsers": response = iama.ListUsers(s3cfg, values) case "ListAccessKeys": response = iama.ListAccessKeys(s3cfg, values) case "CreateUser": response = iama.CreateUser(s3cfg, values) case "DeleteUser": response = iama.DeleteUser(s3cfg, values) case "CreateAccessKey": response = iama.CreateAccessKey(s3cfg, values) case "DeleteAccessKey": response = iama.DeleteAccessKey(s3cfg, values) default: writeErrorResponse(w, s3err.ErrNotImplemented, r.URL) return } writeSuccessResponseXML(w, encodeResponse(response)) }