1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
"github.com/juju/errors"
11
"github.com/juju/testing"
12
jc "github.com/juju/testing/checkers"
13
gc "gopkg.in/check.v1"
14
"gopkg.in/juju/names.v2"
15
"gopkg.in/macaroon.v1"
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"
23
type ChangePasswordCommandSuite struct {
25
mockAPI *mockChangePasswordAPI
26
store jujuclient.ClientStore
29
var _ = gc.Suite(&ChangePasswordCommandSuite{})
31
func (s *ChangePasswordCommandSuite) SetUpTest(c *gc.C) {
32
s.BaseSuite.SetUpTest(c)
33
s.mockAPI = &mockChangePasswordAPI{}
34
s.store = s.BaseSuite.store
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)
45
return ctx, changePasswordCommand.Run(ctx)
48
func (s *ChangePasswordCommandSuite) TestInit(c *gc.C) {
49
for i, test := range []struct {
57
args: []string{"foobar"},
60
args: []string{"--foobar"},
61
errorString: "flag provided but not defined: --foobar",
63
args: []string{"foobar", "extra"},
64
errorString: `unrecognized args: \["extra"\]`,
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)
73
c.Check(err, gc.ErrorMatches, test.errorString)
78
func (s *ChangePasswordCommandSuite) assertAPICalls(c *gc.C, user, pass string) {
80
if user == "current-user@local" {
81
s.mockAPI.CheckCall(c, 0, "CreateLocalLoginMacaroon", names.NewUserTag(user))
84
s.mockAPI.CheckCall(c, offset, "SetPassword", user, pass)
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, `
95
Your password has been updated.
99
func (s *ChangePasswordCommandSuite) TestChangePasswordFail(c *gc.C) {
100
s.mockAPI.SetErrors(nil, errors.New("failed to do something"))
102
c.Assert(err, gc.ErrorMatches, "failed to do something")
103
s.assertAPICalls(c, "current-user@local", "sekrit")
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.AccountDetailsFunc = func(string) (*jujuclient.AccountDetails, error) {
111
return &jujuclient.AccountDetails{"user", "old-password", ""}, nil
113
store.ControllerByNameFunc = func(string) (*jujuclient.ControllerDetails, error) {
114
return &jujuclient.ControllerDetails{}, nil
117
store.SetErrors(nil, errors.New("failed to write"))
120
c.Assert(err, gc.ErrorMatches, "failed to update client credentials: failed to write")
121
s.mockAPI.CheckCallNames(c, "CreateLocalLoginMacaroon") // no SetPassword
124
func (s *ChangePasswordCommandSuite) TestChangeOthersPassword(c *gc.C) {
125
// The checks for user existence and admin rights are tested
126
// at the apiserver level.
127
_, err := s.run(c, "other")
128
c.Assert(err, jc.ErrorIsNil)
129
s.assertAPICalls(c, "other@local", "sekrit")
132
type mockChangePasswordAPI struct {
136
func (m *mockChangePasswordAPI) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) {
137
m.MethodCall(m, "CreateLocalLoginMacaroon", tag)
138
if err := m.NextErr(); err != nil {
141
return fakeLocalLoginMacaroon(tag), nil
144
func (m *mockChangePasswordAPI) SetPassword(username, password string) error {
145
m.MethodCall(m, "SetPassword", username, password)
149
func (*mockChangePasswordAPI) Close() error {
153
func fakeLocalLoginMacaroon(tag names.UserTag) *macaroon.Macaroon {
154
mac, err := macaroon.New([]byte("abcdefghijklmnopqrstuvwx"), tag.Canonical(), "juju")