github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/user/login_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 "bytes" 8 "strings" 9 10 "github.com/juju/cmd" 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/names.v2" 15 16 "github.com/juju/juju/api" 17 apibase "github.com/juju/juju/api/base" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/cmd/juju/user" 20 "github.com/juju/juju/cmd/modelcmd" 21 "github.com/juju/juju/juju" 22 "github.com/juju/juju/jujuclient" 23 ) 24 25 type LoginCommandSuite struct { 26 BaseSuite 27 apiConnection *loginMockAPI 28 29 // apiConnectionParams is set when the mock newAPIConnection 30 // implementation installed by SetUpTest is called. 31 apiConnectionParams juju.NewAPIConnectionParams 32 } 33 34 var _ = gc.Suite(&LoginCommandSuite{}) 35 36 func (s *LoginCommandSuite) SetUpTest(c *gc.C) { 37 s.BaseSuite.SetUpTest(c) 38 s.apiConnection = &loginMockAPI{ 39 controllerTag: names.NewControllerTag(mockControllerUUID), 40 authTag: names.NewUserTag("user@external"), 41 controllerAccess: "superuser", 42 } 43 s.apiConnectionParams = juju.NewAPIConnectionParams{} 44 s.PatchValue(user.NewAPIConnection, func(p juju.NewAPIConnectionParams) (api.Connection, error) { 45 // The account details are modified in place, so take a copy. 46 accountDetails := *p.AccountDetails 47 p.AccountDetails = &accountDetails 48 s.apiConnectionParams = p 49 return s.apiConnection, nil 50 }) 51 s.PatchValue(user.ListModels, func(c api.Connection, userName string) ([]apibase.UserModel, error) { 52 return nil, nil 53 }) 54 s.PatchValue(user.APIOpen, func(c *modelcmd.CommandBase, info *api.Info, opts api.DialOpts) (api.Connection, error) { 55 return s.apiConnection, nil 56 }) 57 s.PatchValue(user.LoginClientStore, s.store) 58 } 59 60 func (s *LoginCommandSuite) TestInitError(c *gc.C) { 61 for i, test := range []struct { 62 args []string 63 stderr string 64 }{{ 65 args: []string{"--foobar"}, 66 stderr: `ERROR option provided but not defined: --foobar\n`, 67 }, { 68 args: []string{"foobar", "extra"}, 69 stderr: `ERROR unrecognized args: \["extra"\]\n`, 70 }} { 71 c.Logf("test %d", i) 72 stdout, stderr, code := runLogin(c, "", test.args...) 73 c.Check(stdout, gc.Equals, "") 74 c.Check(stderr, gc.Matches, test.stderr) 75 c.Assert(code, gc.Equals, 2) 76 } 77 } 78 79 func (s *LoginCommandSuite) TestLogin(c *gc.C) { 80 // When we run login with a current controller, 81 // it will just verify that we can log in, leave 82 // every unchanged and print nothing. 83 stdout, stderr, code := runLogin(c, "") 84 c.Check(code, gc.Equals, 0) 85 c.Check(stdout, gc.Equals, "") 86 c.Check(stderr, gc.Equals, "") 87 s.assertStorePassword(c, "current-user", "old-password", "superuser") 88 c.Assert(s.apiConnectionParams.AccountDetails, jc.DeepEquals, &jujuclient.AccountDetails{ 89 User: "current-user", 90 Password: "old-password", 91 }) 92 } 93 94 func (s *LoginCommandSuite) TestLoginNewUser(c *gc.C) { 95 err := s.store.RemoveAccount("testing") 96 c.Assert(err, jc.ErrorIsNil) 97 stdout, stderr, code := runLogin(c, "", "-u", "new-user") 98 c.Check(stdout, gc.Equals, "") 99 c.Check(stderr, gc.Matches, ` 100 Welcome, new-user. You are now logged into "testing". 101 102 There are no models available(.|\n)*`[1:]) 103 c.Assert(code, gc.Equals, 0) 104 s.assertStorePassword(c, "new-user", "", "superuser") 105 c.Assert(s.apiConnectionParams.AccountDetails, jc.DeepEquals, &jujuclient.AccountDetails{ 106 User: "new-user", 107 }) 108 } 109 110 func (s *LoginCommandSuite) TestLoginAlreadyLoggedInSameUser(c *gc.C) { 111 stdout, stderr, code := runLogin(c, "", "-u", "current-user") 112 c.Check(stdout, gc.Equals, "") 113 c.Check(stderr, gc.Equals, "") 114 c.Assert(code, gc.Equals, 0) 115 } 116 117 func (s *LoginCommandSuite) TestLoginWithOneAvailableModel(c *gc.C) { 118 s.PatchValue(user.ListModels, func(c api.Connection, userName string) ([]apibase.UserModel, error) { 119 return []apibase.UserModel{{ 120 Name: "foo", 121 UUID: "some-uuid", 122 Owner: "bob", 123 Type: "iaas", 124 }}, nil 125 }) 126 err := s.store.RemoveAccount("testing") 127 c.Assert(err, jc.ErrorIsNil) 128 stdout, stderr, code := runLogin(c, "") 129 c.Assert(code, gc.Equals, 0) 130 c.Check(stdout, gc.Equals, "") 131 c.Check(stderr, gc.Matches, `Welcome, user@external. You are now logged into "testing". 132 133 Current model set to "bob/foo". 134 `) 135 } 136 137 func (s *LoginCommandSuite) TestLoginWithSeveralAvailableModels(c *gc.C) { 138 s.PatchValue(user.ListModels, func(c api.Connection, userName string) ([]apibase.UserModel, error) { 139 return []apibase.UserModel{{ 140 Name: "foo", 141 UUID: "some-uuid", 142 Owner: "bob", 143 Type: "iaas", 144 }, { 145 Name: "bar", 146 UUID: "some-uuid", 147 Owner: "alice", 148 Type: "iaas", 149 }}, nil 150 }) 151 err := s.store.RemoveAccount("testing") 152 c.Assert(err, jc.ErrorIsNil) 153 stdout, stderr, code := runLogin(c, "") 154 c.Assert(code, gc.Equals, 0) 155 c.Check(stdout, gc.Equals, "") 156 c.Check(stderr, gc.Matches, `Welcome, user@external. You are now logged into "testing". 157 158 There are 2 models available. Use "juju switch" to select 159 one of them: 160 - juju switch alice/bar 161 - juju switch bob/foo 162 `) 163 } 164 165 func (s *LoginCommandSuite) TestLoginWithNonExistentController(c *gc.C) { 166 stdout, stderr, code := runLogin(c, "", "-c", "something") 167 c.Assert(code, gc.Equals, 1) 168 c.Check(stdout, gc.Equals, "") 169 c.Check(stderr, gc.Matches, `ERROR controller "something" does not exist\n`) 170 } 171 172 func (s *LoginCommandSuite) TestLoginWithNoCurrentController(c *gc.C) { 173 s.store.CurrentControllerName = "" 174 stdout, stderr, code := runLogin(c, "") 175 c.Assert(code, gc.Equals, 1) 176 c.Check(stdout, gc.Equals, "") 177 c.Check(stderr, gc.Matches, `ERROR no current controller\n`) 178 } 179 180 func (s *LoginCommandSuite) TestLoginAlreadyLoggedInDifferentUser(c *gc.C) { 181 stdout, stderr, code := runLogin(c, "", "-u", "other-user") 182 c.Check(stdout, gc.Equals, "") 183 c.Check(stderr, gc.Equals, ` 184 ERROR cannot log into controller "testing": already logged in as current-user. 185 186 Run "juju logout" first before attempting to log in as a different user. 187 `[1:]) 188 c.Assert(code, gc.Equals, 1) 189 } 190 191 func (s *LoginCommandSuite) TestLoginWithExistingInvalidPassword(c *gc.C) { 192 call := 0 193 *user.NewAPIConnection = func(p juju.NewAPIConnectionParams) (api.Connection, error) { 194 call++ 195 switch call { 196 case 1: 197 // First time: try to log in with existing details. 198 c.Check(p.AccountDetails.User, gc.Equals, "current-user") 199 c.Check(p.AccountDetails.Password, gc.Equals, "old-password") 200 return nil, errors.Unauthorizedf("cannot login with that silly old password") 201 case 2: 202 // Second time: try external-user auth. 203 c.Check(p.AccountDetails.User, gc.Equals, "") 204 c.Check(p.AccountDetails.Password, gc.Equals, "") 205 return nil, params.Error{ 206 Code: params.CodeNoCreds, 207 Message: params.CodeNoCreds, 208 } 209 case 3: 210 // Third time: empty password: (the real 211 // NewAPIConnection would prompt for it) 212 c.Check(p.AccountDetails.User, gc.Equals, "other-user") 213 c.Check(p.AccountDetails.Password, gc.Equals, "") 214 return s.apiConnection, nil 215 default: 216 c.Errorf("NewAPIConnection called too many times") 217 return nil, errors.Errorf("too many calls") 218 } 219 } 220 stdout, stderr, code := runLogin(c, "other-user\n") 221 c.Check(code, gc.Equals, 0) 222 c.Check(stdout, gc.Equals, "") 223 c.Check(stderr, gc.Matches, `username: Welcome, other-user. (.|\n)+`) 224 } 225 226 func (s *LoginCommandSuite) TestLoginWithMacaroons(c *gc.C) { 227 err := s.store.RemoveAccount("testing") 228 c.Assert(err, jc.ErrorIsNil) 229 stdout, stderr, code := runLogin(c, "") 230 c.Check(stderr, gc.Matches, ` 231 Welcome, user@external. You are now logged into "testing". 232 233 There are no models available(.|\n)*`[1:]) 234 c.Check(stdout, gc.Equals, ``) 235 c.Assert(code, gc.Equals, 0) 236 c.Assert(s.apiConnectionParams.AccountDetails, jc.DeepEquals, &jujuclient.AccountDetails{}) 237 } 238 239 func (s *LoginCommandSuite) TestLoginWithMacaroonsNotSupported(c *gc.C) { 240 err := s.store.RemoveAccount("testing") 241 c.Assert(err, jc.ErrorIsNil) 242 *user.NewAPIConnection = func(p juju.NewAPIConnectionParams) (api.Connection, error) { 243 if !c.Check(p.AccountDetails, gc.NotNil) { 244 return nil, errors.New("no account details") 245 } 246 if p.AccountDetails.User == "" && p.AccountDetails.Password == "" { 247 return nil, ¶ms.Error{Code: params.CodeNoCreds, Message: "barf"} 248 } 249 c.Check(p.AccountDetails.User, gc.Equals, "new-user") 250 return s.apiConnection, nil 251 } 252 stdout, stderr, code := runLogin(c, "new-user\n") 253 c.Check(stdout, gc.Equals, ``) 254 c.Check(stderr, gc.Matches, ` 255 username: Welcome, new-user. You are now logged into "testing". 256 257 There are no models available(.|\n)*`[1:]) 258 c.Assert(code, gc.Equals, 0) 259 } 260 261 func runLogin(c *gc.C, stdin string, args ...string) (stdout, stderr string, errCode int) { 262 c.Logf("in LoginControllerSuite.run") 263 var stdoutBuf, stderrBuf bytes.Buffer 264 ctxt := &cmd.Context{ 265 Dir: c.MkDir(), 266 Stdin: strings.NewReader(stdin), 267 Stdout: &stdoutBuf, 268 Stderr: &stderrBuf, 269 } 270 exitCode := cmd.Main(user.NewLoginCommand(), ctxt, args) 271 return stdoutBuf.String(), stderrBuf.String(), exitCode 272 }