github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "path/filepath" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/cmd/envcmd" 15 "github.com/juju/juju/cmd/juju/user" 16 "github.com/juju/juju/environs/configstore" 17 "github.com/juju/juju/testing" 18 ) 19 20 type ChangePasswordCommandSuite struct { 21 BaseSuite 22 mockAPI *mockChangePasswordAPI 23 mockEnvironInfo *mockEnvironInfo 24 randomPassword string 25 serverFilename string 26 } 27 28 var _ = gc.Suite(&ChangePasswordCommandSuite{}) 29 30 func (s *ChangePasswordCommandSuite) SetUpTest(c *gc.C) { 31 s.BaseSuite.SetUpTest(c) 32 s.mockAPI = &mockChangePasswordAPI{} 33 s.mockEnvironInfo = &mockEnvironInfo{ 34 creds: configstore.APICredentials{"user-name", "password"}, 35 } 36 s.randomPassword = "" 37 s.serverFilename = "" 38 s.PatchValue(user.RandomPasswordNotify, func(pwd string) { 39 s.randomPassword = pwd 40 }) 41 s.PatchValue(user.ServerFileNotify, func(filename string) { 42 s.serverFilename = filename 43 }) 44 } 45 46 func (s *ChangePasswordCommandSuite) run(c *gc.C, args ...string) (*cmd.Context, error) { 47 changePasswordCommand := envcmd.WrapSystem(user.NewChangePasswordCommand(s.mockAPI, s.mockEnvironInfo)) 48 return testing.RunCommand(c, changePasswordCommand, args...) 49 } 50 51 func (s *ChangePasswordCommandSuite) TestInit(c *gc.C) { 52 for i, test := range []struct { 53 args []string 54 user string 55 outPath string 56 generate bool 57 errorString string 58 }{ 59 { 60 // no args is fine 61 }, { 62 args: []string{"--generate"}, 63 generate: true, 64 }, { 65 args: []string{"foobar"}, 66 user: "foobar", 67 generate: true, 68 outPath: "foobar.server", 69 }, { 70 args: []string{"foobar", "--generate"}, 71 user: "foobar", 72 generate: true, 73 outPath: "foobar.server", 74 }, { 75 args: []string{"foobar", "--output", "somefile"}, 76 user: "foobar", 77 generate: true, 78 outPath: "somefile", 79 }, { 80 args: []string{"--foobar"}, 81 errorString: "flag provided but not defined: --foobar", 82 }, { 83 args: []string{"foobar", "extra"}, 84 errorString: `unrecognized args: \["extra"\]`, 85 }, { 86 args: []string{"--output", "somefile"}, 87 errorString: "output is only a valid option when changing another user's password", 88 }, 89 } { 90 c.Logf("test %d", i) 91 command := &user.ChangePasswordCommand{} 92 err := testing.InitCommand(command, test.args) 93 if test.errorString == "" { 94 c.Check(command.User, gc.Equals, test.user) 95 c.Check(command.OutPath, gc.Equals, test.outPath) 96 c.Check(command.Generate, gc.Equals, test.generate) 97 } else { 98 c.Check(err, gc.ErrorMatches, test.errorString) 99 } 100 } 101 } 102 103 func (s *ChangePasswordCommandSuite) assertRandomPassword(c *gc.C) { 104 c.Assert(s.mockAPI.password, gc.Equals, s.randomPassword) 105 c.Assert(s.mockAPI.password, gc.HasLen, 24) 106 } 107 108 func (s *ChangePasswordCommandSuite) assertPasswordFromReadPass(c *gc.C) { 109 c.Assert(s.mockAPI.password, gc.Equals, "sekrit") 110 } 111 112 func (s *ChangePasswordCommandSuite) TestChangePassword(c *gc.C) { 113 context, err := s.run(c) 114 c.Assert(err, jc.ErrorIsNil) 115 c.Assert(s.mockAPI.username, gc.Equals, "user-name") 116 s.assertPasswordFromReadPass(c) 117 expected := ` 118 password: 119 type password again: 120 `[1:] 121 c.Assert(testing.Stdout(context), gc.Equals, expected) 122 c.Assert(testing.Stderr(context), gc.Equals, "Your password has been updated.\n") 123 } 124 125 func (s *ChangePasswordCommandSuite) TestChangePasswordGenerate(c *gc.C) { 126 context, err := s.run(c, "--generate") 127 c.Assert(err, jc.ErrorIsNil) 128 c.Assert(s.mockAPI.username, gc.Equals, "user-name") 129 s.assertRandomPassword(c) 130 c.Assert(testing.Stderr(context), gc.Equals, "Your password has been updated.\n") 131 } 132 133 func (s *ChangePasswordCommandSuite) TestChangePasswordFail(c *gc.C) { 134 s.mockAPI.failMessage = "failed to do something" 135 s.mockAPI.failOps = []bool{true, false} 136 _, err := s.run(c, "--generate") 137 c.Assert(err, gc.ErrorMatches, "failed to do something") 138 c.Assert(s.mockAPI.username, gc.Equals, "") 139 } 140 141 // The first write fails, so we try to revert the password which succeeds 142 func (s *ChangePasswordCommandSuite) TestRevertPasswordAfterFailedWrite(c *gc.C) { 143 // Fail to Write the new jenv file 144 s.mockEnvironInfo.failMessage = "failed to write" 145 _, err := s.run(c, "--generate") 146 c.Assert(err, gc.ErrorMatches, "failed to write new password to environments file: failed to write") 147 // Last api call was to set the password back to the original. 148 c.Assert(s.mockAPI.password, gc.Equals, "password") 149 } 150 151 // SetPassword api works the first time, but the write fails, our second call to set password fails 152 func (s *ChangePasswordCommandSuite) TestChangePasswordRevertApiFails(c *gc.C) { 153 s.mockAPI.failMessage = "failed to do something" 154 s.mockEnvironInfo.failMessage = "failed to write" 155 s.mockAPI.failOps = []bool{false, true} 156 _, err := s.run(c, "--generate") 157 c.Assert(err, gc.ErrorMatches, "failed to set password back: failed to do something") 158 } 159 160 func (s *ChangePasswordCommandSuite) TestChangeOthersPassword(c *gc.C) { 161 // The checks for user existence and admin rights are tested 162 // at the apiserver level. 163 context, err := s.run(c, "other") 164 c.Assert(err, jc.ErrorIsNil) 165 c.Assert(s.mockAPI.username, gc.Equals, "other") 166 s.assertRandomPassword(c) 167 s.assertServerFileMatches(c, s.serverFilename, "other", s.randomPassword) 168 expected := ` 169 server file written to .*other.server 170 `[1:] 171 c.Assert(testing.Stderr(context), gc.Matches, expected) 172 } 173 174 func (s *ChangePasswordCommandSuite) TestChangeOthersPasswordWithFile(c *gc.C) { 175 // The checks for user existence and admin rights are tested 176 // at the apiserver level. 177 filename := filepath.Join(c.MkDir(), "test.result") 178 _, err := s.run(c, "other", "-o", filename) 179 c.Assert(err, jc.ErrorIsNil) 180 s.assertRandomPassword(c) 181 c.Assert(filepath.Base(s.serverFilename), gc.Equals, "test.result") 182 s.assertServerFileMatches(c, s.serverFilename, "other", s.randomPassword) 183 } 184 185 type mockEnvironInfo struct { 186 failMessage string 187 creds configstore.APICredentials 188 } 189 190 func (m *mockEnvironInfo) Write() error { 191 if m.failMessage != "" { 192 return errors.New(m.failMessage) 193 } 194 return nil 195 } 196 197 func (m *mockEnvironInfo) SetAPICredentials(creds configstore.APICredentials) { 198 m.creds = creds 199 } 200 201 func (m *mockEnvironInfo) APICredentials() configstore.APICredentials { 202 return m.creds 203 } 204 205 type mockChangePasswordAPI struct { 206 failMessage string 207 currentOp int 208 failOps []bool // Can be used to make the call pass/ fail in a known order 209 username string 210 password string 211 } 212 213 func (m *mockChangePasswordAPI) SetPassword(username, password string) error { 214 if len(m.failOps) > 0 && m.failOps[m.currentOp] { 215 m.currentOp++ 216 return errors.New(m.failMessage) 217 } 218 m.currentOp++ 219 m.username = username 220 m.password = password 221 return nil 222 } 223 224 func (*mockChangePasswordAPI) Close() error { 225 return nil 226 }