1
// Package iamtest implements a fake IAM provider with the capability of
2
// inducing errors on any given operation, and retrospectively determining what
3
// operations have been carried out.
10
"launchpad.net/goamz/iam"
24
// Server implements an IAM simulator for use in tests.
31
accessKeys []iam.AccessKey
32
userPolicies []iam.UserPolicy
36
func NewServer() (*Server, error) {
37
l, err := net.Listen("tcp", "localhost:0")
39
return nil, fmt.Errorf("cannot listen on localhost: %v", err)
43
url: "http://" + l.Addr().String(),
45
go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
51
// Quit closes down the server.
52
func (srv *Server) Quit() error {
53
return srv.listener.Close()
56
// URL returns a URL for the server.
57
func (srv *Server) URL() string {
61
type xmlErrors struct {
62
XMLName string `xml:"ErrorResponse"`
66
func (srv *Server) error(w http.ResponseWriter, err *iam.Error) {
67
w.WriteHeader(err.StatusCode)
68
xmlErr := xmlErrors{Error: *err}
69
if e := xml.NewEncoder(w).Encode(xmlErr); e != nil {
74
func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) {
77
defer srv.mutex.Unlock()
78
action := req.FormValue("Action")
80
srv.error(w, &iam.Error{
82
Code: "MissingAction",
83
Message: "Missing action",
86
if a, ok := actions[action]; ok {
87
reqId := fmt.Sprintf("req%0X", srv.reqId)
89
if resp, err := a(srv, w, req, reqId); err == nil {
90
if err := xml.NewEncoder(w).Encode(resp); err != nil {
96
srv.error(w, err.(*iam.Error))
102
srv.error(w, &iam.Error{
104
Code: "InvalidAction",
105
Message: "Invalid action: " + action,
110
func (srv *Server) createUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
111
if err := srv.validate(req, []string{"UserName"}); err != nil {
114
path := req.FormValue("Path")
118
name := req.FormValue("UserName")
119
for _, user := range srv.users {
120
if user.Name == name {
121
return nil, &iam.Error{
123
Code: "EntityAlreadyExists",
124
Message: fmt.Sprintf("User with name %s already exists.", name),
129
Id: "USER" + reqId + "EXAMPLE",
130
Arn: fmt.Sprintf("arn:aws:iam:::123456789012:user%s%s", path, name),
134
srv.users = append(srv.users, user)
135
return iam.CreateUserResp{
141
func (srv *Server) getUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
142
if err := srv.validate(req, []string{"UserName"}); err != nil {
145
name := req.FormValue("UserName")
146
index, err := srv.findUser(name)
150
return iam.GetUserResp{RequestId: reqId, User: srv.users[index]}, nil
153
func (srv *Server) deleteUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
154
if err := srv.validate(req, []string{"UserName"}); err != nil {
157
name := req.FormValue("UserName")
158
index, err := srv.findUser(name)
162
copy(srv.users[index:], srv.users[index+1:])
163
srv.users = srv.users[:len(srv.users)-1]
164
return iam.SimpleResp{RequestId: reqId}, nil
167
func (srv *Server) createAccessKey(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
168
if err := srv.validate(req, []string{"UserName"}); err != nil {
171
userName := req.FormValue("UserName")
172
if _, err := srv.findUser(userName); err != nil {
175
key := iam.AccessKey{
176
Id: fmt.Sprintf("%s%d", userName, len(srv.accessKeys)),
181
srv.accessKeys = append(srv.accessKeys, key)
182
return iam.CreateAccessKeyResp{RequestId: reqId, AccessKey: key}, nil
185
func (srv *Server) deleteAccessKey(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
186
if err := srv.validate(req, []string{"AccessKeyId", "UserName"}); err != nil {
189
key := req.FormValue("AccessKeyId")
191
for i, ak := range srv.accessKeys {
198
return nil, &iam.Error{
200
Code: "NoSuchEntity",
201
Message: "No such key.",
204
copy(srv.accessKeys[index:], srv.accessKeys[index+1:])
205
srv.accessKeys = srv.accessKeys[:len(srv.accessKeys)-1]
206
return iam.SimpleResp{RequestId: reqId}, nil
209
func (srv *Server) listAccessKeys(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
210
if err := srv.validate(req, []string{"UserName"}); err != nil {
213
userName := req.FormValue("UserName")
214
if _, err := srv.findUser(userName); err != nil {
217
var keys []iam.AccessKey
218
for _, k := range srv.accessKeys {
219
if k.UserName == userName {
220
keys = append(keys, k)
223
return iam.AccessKeysResp{
229
func (srv *Server) createGroup(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
230
if err := srv.validate(req, []string{"GroupName"}); err != nil {
233
name := req.FormValue("GroupName")
234
path := req.FormValue("Path")
235
for _, group := range srv.groups {
236
if group.Name == name {
237
return nil, &iam.Error{
239
Code: "EntityAlreadyExists",
240
Message: fmt.Sprintf("Group with name %s already exists.", name),
245
Id: "GROUP " + reqId + "EXAMPLE",
246
Arn: fmt.Sprintf("arn:aws:iam:::123456789012:group%s%s", path, name),
250
srv.groups = append(srv.groups, group)
251
return iam.CreateGroupResp{
257
func (srv *Server) listGroups(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
258
pathPrefix := req.FormValue("PathPrefix")
259
if pathPrefix == "" {
260
return iam.GroupsResp{
265
var groups []iam.Group
266
for _, group := range srv.groups {
267
if strings.HasPrefix(group.Path, pathPrefix) {
268
groups = append(groups, group)
271
return iam.GroupsResp{
277
func (srv *Server) deleteGroup(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
278
if err := srv.validate(req, []string{"GroupName"}); err != nil {
281
name := req.FormValue("GroupName")
283
for i, group := range srv.groups {
284
if group.Name == name {
290
return nil, &iam.Error{
292
Code: "NoSuchEntity",
293
Message: fmt.Sprintf("The group with name %s cannot be found.", name),
296
copy(srv.groups[index:], srv.groups[index+1:])
297
srv.groups = srv.groups[:len(srv.groups)-1]
298
return iam.SimpleResp{RequestId: reqId}, nil
301
func (srv *Server) putUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
302
if err := srv.validate(req, []string{"UserName", "PolicyDocument", "PolicyName"}); err != nil {
306
policyName := req.FormValue("PolicyName")
307
userName := req.FormValue("UserName")
308
for _, policy := range srv.userPolicies {
309
if policyName == policy.Name && userName == policy.UserName {
315
policy := iam.UserPolicy{
318
Document: req.FormValue("PolicyDocument"),
321
if err := json.Unmarshal([]byte(policy.Document), &dumb); err != nil {
322
return nil, &iam.Error{
324
Code: "MalformedPolicyDocument",
325
Message: "Malformed policy document",
328
srv.userPolicies = append(srv.userPolicies, policy)
330
return iam.SimpleResp{RequestId: reqId}, nil
333
func (srv *Server) deleteUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
334
if err := srv.validate(req, []string{"UserName", "PolicyName"}); err != nil {
337
policyName := req.FormValue("PolicyName")
338
userName := req.FormValue("UserName")
340
for i, policy := range srv.userPolicies {
341
if policyName == policy.Name && userName == policy.UserName {
347
return nil, &iam.Error{
349
Code: "NoSuchEntity",
350
Message: "No such user policy",
353
copy(srv.userPolicies[index:], srv.userPolicies[index+1:])
354
srv.userPolicies = srv.userPolicies[:len(srv.userPolicies)-1]
355
return iam.SimpleResp{RequestId: reqId}, nil
358
func (srv *Server) getUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) {
359
if err := srv.validate(req, []string{"UserName", "PolicyName"}); err != nil {
362
policyName := req.FormValue("PolicyName")
363
userName := req.FormValue("UserName")
365
for i, policy := range srv.userPolicies {
366
if policyName == policy.Name && userName == policy.UserName {
372
return nil, &iam.Error{
374
Code: "NoSuchEntity",
375
Message: "No such user policy",
378
return iam.GetUserPolicyResp{
379
Policy: srv.userPolicies[index],
384
func (srv *Server) findUser(userName string) (int, error) {
389
for i, user := range srv.users {
390
if user.Name == userName {
398
Code: "NoSuchEntity",
399
Message: fmt.Sprintf("The user with name %s cannot be found.", userName),
405
// Validates the presence of required request parameters.
406
func (srv *Server) validate(req *http.Request, required []string) error {
407
for _, r := range required {
408
if req.FormValue(r) == "" {
411
Code: "InvalidParameterCombination",
412
Message: fmt.Sprintf("%s is required.", r),
419
var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) (interface{}, error){
420
"CreateUser": (*Server).createUser,
421
"DeleteUser": (*Server).deleteUser,
422
"GetUser": (*Server).getUser,
423
"CreateAccessKey": (*Server).createAccessKey,
424
"DeleteAccessKey": (*Server).deleteAccessKey,
425
"ListAccessKeys": (*Server).listAccessKeys,
426
"PutUserPolicy": (*Server).putUserPolicy,
427
"DeleteUserPolicy": (*Server).deleteUserPolicy,
428
"GetUserPolicy": (*Server).getUserPolicy,
429
"CreateGroup": (*Server).createGroup,
430
"DeleteGroup": (*Server).deleteGroup,
431
"ListGroups": (*Server).listGroups,