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  }