|
@ -2,44 +2,179 @@ package iamapi |
|
|
|
|
|
|
|
|
import ( |
|
|
import ( |
|
|
"encoding/xml" |
|
|
"encoding/xml" |
|
|
|
|
|
"fmt" |
|
|
"github.com/chrislusf/seaweedfs/weed/glog" |
|
|
"github.com/chrislusf/seaweedfs/weed/glog" |
|
|
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb" |
|
|
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb" |
|
|
"github.com/chrislusf/seaweedfs/weed/s3api/s3err" |
|
|
"github.com/chrislusf/seaweedfs/weed/s3api/s3err" |
|
|
|
|
|
"math/rand" |
|
|
"net/http" |
|
|
"net/http" |
|
|
"net/url" |
|
|
"net/url" |
|
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
// "github.com/aws/aws-sdk-go/aws"
|
|
|
// "github.com/aws/aws-sdk-go/aws"
|
|
|
"github.com/aws/aws-sdk-go/service/iam" |
|
|
"github.com/aws/aws-sdk-go/service/iam" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
const ( |
|
|
const ( |
|
|
version = "2010-05-08" |
|
|
|
|
|
|
|
|
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 { |
|
|
type ListUsersResponse struct { |
|
|
|
|
|
CommonResponse |
|
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListUsersResponse"` |
|
|
XMLName xml.Name `xml:"https://iam.amazonaws.com/doc/2010-05-08/ ListUsersResponse"` |
|
|
ListUsersResult struct { |
|
|
ListUsersResult struct { |
|
|
Users []*iam.User `xml:"Users>member"` |
|
|
Users []*iam.User `xml:"Users>member"` |
|
|
IsTruncated bool `xml:"IsTruncated"` |
|
|
IsTruncated bool `xml:"IsTruncated"` |
|
|
} `xml:"ListUsersResult"` |
|
|
} `xml:"ListUsersResult"` |
|
|
ResponseMetadata struct { |
|
|
|
|
|
RequestId string `xml:"RequestId"` |
|
|
|
|
|
} `xml:"ResponseMetadata"` |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// {'Action': 'CreateUser', 'Version': '2010-05-08', 'UserName': 'Bob'}
|
|
|
|
|
|
// {'Action': 'ListUsers', 'Version': '2010-05-08'}
|
|
|
|
|
|
func (iama *IamApiServer) ListUsers(s3cfg *iam_pb.S3ApiConfiguration, values url.Values) ListUsersResponse { |
|
|
|
|
|
glog.Info("Do ListUsers") |
|
|
|
|
|
resp := ListUsersResponse{} |
|
|
|
|
|
|
|
|
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 { |
|
|
for _, ident := range s3cfg.Identities { |
|
|
resp.ListUsersResult.Users = append(resp.ListUsersResult.Users, &iam.User{UserName: &ident.Name}) |
|
|
resp.ListUsersResult.Users = append(resp.ListUsersResult.Users, &iam.User{UserName: &ident.Name}) |
|
|
} |
|
|
} |
|
|
return resp |
|
|
return resp |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func (iama *IamApiServer) ListAccessKeys(values url.Values) ListUsersResponse { |
|
|
|
|
|
return ListUsersResponse{} |
|
|
|
|
|
|
|
|
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) { |
|
|
func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) { |
|
@ -60,7 +195,15 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) { |
|
|
case "ListUsers": |
|
|
case "ListUsers": |
|
|
response = iama.ListUsers(s3cfg, values) |
|
|
response = iama.ListUsers(s3cfg, values) |
|
|
case "ListAccessKeys": |
|
|
case "ListAccessKeys": |
|
|
response = iama.ListAccessKeys(values) |
|
|
|
|
|
|
|
|
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: |
|
|
default: |
|
|
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL) |
|
|
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL) |
|
|
return |
|
|
return |
|
|