github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/user/change_password_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package user_test 5 6 import ( 7 "strings" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/macaroon.v1" 16 17 "github.com/juju/juju/cmd/juju/user" 18 "github.com/juju/juju/jujuclient" 19 "github.com/juju/juju/jujuclient/jujuclienttesting" 20 coretesting "github.com/juju/juju/testing" 21 ) 22 23 type ChangePasswordCommandSuite struct { 24 BaseSuite 25 mockAPI *mockChangePasswordAPI 26 store jujuclient.ClientStore 27 } 28 29 var _ = gc.Suite(&ChangePasswordCommandSuite{}) 30 31 func (s *ChangePasswordCommandSuite) SetUpTest(c *gc.C) { 32 s.BaseSuite.SetUpTest(c) 33 s.mockAPI = &mockChangePasswordAPI{} 34 s.store = s.BaseSuite.store 35 } 36 37 func (s *ChangePasswordCommandSuite) run(c *gc.C, args ...string) (*cmd.Context, error) { 38 changePasswordCommand, _ := user.NewChangePasswordCommandForTest(s.mockAPI, s.store) 39 ctx := coretesting.Context(c) 40 ctx.Stdin = strings.NewReader("sekrit\nsekrit\n") 41 err := coretesting.InitCommand(changePasswordCommand, args) 42 if err != nil { 43 return ctx, err 44 } 45 return ctx, changePasswordCommand.Run(ctx) 46 } 47 48 func (s *ChangePasswordCommandSuite) TestInit(c *gc.C) { 49 for i, test := range []struct { 50 args []string 51 user string 52 errorString string 53 }{ 54 { 55 // no args is fine 56 }, { 57 args: []string{"foobar"}, 58 user: "foobar", 59 }, { 60 args: []string{"--foobar"}, 61 errorString: "flag provided but not defined: --foobar", 62 }, { 63 args: []string{"foobar", "extra"}, 64 errorString: `unrecognized args: \["extra"\]`, 65 }, 66 } { 67 c.Logf("test %d", i) 68 wrappedCommand, command := user.NewChangePasswordCommandForTest(nil, s.store) 69 err := coretesting.InitCommand(wrappedCommand, test.args) 70 if test.errorString == "" { 71 c.Check(command.User, gc.Equals, test.user) 72 } else { 73 c.Check(err, gc.ErrorMatches, test.errorString) 74 } 75 } 76 } 77 78 func (s *ChangePasswordCommandSuite) assertAPICalls(c *gc.C, user, pass string) { 79 var offset int 80 if user == "current-user@local" { 81 s.mockAPI.CheckCall(c, 0, "CreateLocalLoginMacaroon", names.NewUserTag(user)) 82 offset += 1 83 } 84 s.mockAPI.CheckCall(c, offset, "SetPassword", user, pass) 85 } 86 87 func (s *ChangePasswordCommandSuite) TestChangePassword(c *gc.C) { 88 context, err := s.run(c) 89 c.Assert(err, jc.ErrorIsNil) 90 s.assertAPICalls(c, "current-user@local", "sekrit") 91 c.Assert(coretesting.Stdout(context), gc.Equals, "") 92 c.Assert(coretesting.Stderr(context), gc.Equals, ` 93 password: 94 type password again: 95 Your password has been updated. 96 `[1:]) 97 } 98 99 func (s *ChangePasswordCommandSuite) TestChangePasswordFail(c *gc.C) { 100 s.mockAPI.SetErrors(nil, errors.New("failed to do something")) 101 _, err := s.run(c) 102 c.Assert(err, gc.ErrorMatches, "failed to do something") 103 s.assertAPICalls(c, "current-user@local", "sekrit") 104 } 105 106 // We create a macaroon, but fail to write it to accounts.yaml. 107 // We should not call SetPassword subsequently. 108 func (s *ChangePasswordCommandSuite) TestNoSetPasswordAfterFailedWrite(c *gc.C) { 109 store := jujuclienttesting.NewStubStore() 110 store.CurrentAccountFunc = func(string) (string, error) { 111 return "account-name", nil 112 } 113 store.AccountByNameFunc = func(string, string) (*jujuclient.AccountDetails, error) { 114 return &jujuclient.AccountDetails{"user", "old-password", ""}, nil 115 } 116 store.ControllerByNameFunc = func(string) (*jujuclient.ControllerDetails, error) { 117 return &jujuclient.ControllerDetails{}, nil 118 } 119 s.store = store 120 store.SetErrors(errors.New("failed to write")) 121 122 _, err := s.run(c) 123 c.Assert(err, gc.ErrorMatches, "failed to update client credentials: failed to write") 124 s.mockAPI.CheckCallNames(c, "CreateLocalLoginMacaroon") // no SetPassword 125 } 126 127 func (s *ChangePasswordCommandSuite) TestChangeOthersPassword(c *gc.C) { 128 // The checks for user existence and admin rights are tested 129 // at the apiserver level. 130 _, err := s.run(c, "other") 131 c.Assert(err, jc.ErrorIsNil) 132 s.assertAPICalls(c, "other@local", "sekrit") 133 } 134 135 type mockChangePasswordAPI struct { 136 testing.Stub 137 } 138 139 func (m *mockChangePasswordAPI) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) { 140 m.MethodCall(m, "CreateLocalLoginMacaroon", tag) 141 if err := m.NextErr(); err != nil { 142 return nil, err 143 } 144 return fakeLocalLoginMacaroon(tag), nil 145 } 146 147 func (m *mockChangePasswordAPI) SetPassword(username, password string) error { 148 m.MethodCall(m, "SetPassword", username, password) 149 return m.NextErr() 150 } 151 152 func (*mockChangePasswordAPI) Close() error { 153 return nil 154 } 155 156 func fakeLocalLoginMacaroon(tag names.UserTag) *macaroon.Macaroon { 157 mac, err := macaroon.New([]byte("abcdefghijklmnopqrstuvwx"), tag.Canonical(), "juju") 158 if err != nil { 159 panic(err) 160 } 161 return mac 162 }