github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/cmd/cmdtesting"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/api"
    17  	"github.com/juju/juju/cmd/juju/user"
    18  	"github.com/juju/juju/juju"
    19  	"github.com/juju/juju/jujuclient"
    20  )
    21  
    22  type ChangePasswordCommandSuite struct {
    23  	BaseSuite
    24  	mockAPI *mockChangePasswordAPI
    25  	store   jujuclient.ClientStore
    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{version: 2}
    33  	s.store = s.BaseSuite.store
    34  }
    35  
    36  func (s *ChangePasswordCommandSuite) run(c *gc.C, args ...string) (*cmd.Context, *juju.NewAPIConnectionParams, error) {
    37  	var argsOut juju.NewAPIConnectionParams
    38  	newAPIConnection := func(args juju.NewAPIConnectionParams) (api.Connection, error) {
    39  		argsOut = args
    40  		return mockAPIConnection{}, nil
    41  	}
    42  	changePasswordCommand, _ := user.NewChangePasswordCommandForTest(
    43  		newAPIConnection, s.mockAPI, s.store,
    44  	)
    45  	ctx := cmdtesting.Context(c)
    46  	ctx.Stdin = strings.NewReader("sekrit\nsekrit\n")
    47  	err := cmdtesting.InitCommand(changePasswordCommand, args)
    48  	if err != nil {
    49  		return ctx, nil, err
    50  	}
    51  	return ctx, &argsOut, changePasswordCommand.Run(ctx)
    52  }
    53  
    54  func (s *ChangePasswordCommandSuite) TestInit(c *gc.C) {
    55  	for i, test := range []struct {
    56  		args        []string
    57  		user        string
    58  		reset       bool
    59  		errorString string
    60  	}{
    61  		{
    62  			// no args is fine
    63  		}, {
    64  			args: []string{"foobar"},
    65  			user: "foobar",
    66  		}, {
    67  			args:        []string{"--foobar"},
    68  			errorString: "option provided but not defined: --foobar",
    69  		}, {
    70  			args:  []string{"--reset"},
    71  			reset: true,
    72  		}, {
    73  			args:        []string{"foobar", "extra"},
    74  			errorString: `unrecognized args: \["extra"\]`,
    75  		},
    76  	} {
    77  		c.Logf("test %d", i)
    78  		wrappedCommand, command := user.NewChangePasswordCommandForTest(nil, nil, s.store)
    79  		err := cmdtesting.InitCommand(wrappedCommand, test.args)
    80  		if test.errorString == "" {
    81  			c.Check(command.User, gc.Equals, test.user)
    82  			c.Check(command.Reset, gc.Equals, test.reset)
    83  		} else {
    84  			c.Check(err, gc.ErrorMatches, test.errorString)
    85  		}
    86  	}
    87  }
    88  
    89  func (s *ChangePasswordCommandSuite) assertAPICalls(c *gc.C, user, pass string) {
    90  	s.mockAPI.CheckCall(c, 0, "SetPassword", user, pass)
    91  }
    92  
    93  func (s *ChangePasswordCommandSuite) TestChangePassword(c *gc.C) {
    94  	context, args, err := s.run(c)
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	s.assertAPICalls(c, "current-user", "sekrit")
    97  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
    98  	c.Assert(cmdtesting.Stderr(context), gc.Equals, `
    99  new password: 
   100  type new password again: 
   101  Your password has been changed.
   102  `[1:])
   103  	// The command should have logged in without a password to get a macaroon.
   104  	c.Assert(args.AccountDetails, jc.DeepEquals, &jujuclient.AccountDetails{
   105  		User: "current-user",
   106  	})
   107  }
   108  
   109  func (s *ChangePasswordCommandSuite) TestChangePasswordFail(c *gc.C) {
   110  	s.mockAPI.SetErrors(errors.New("failed to do something"))
   111  	_, _, err := s.run(c)
   112  	c.Assert(err, gc.ErrorMatches, "failed to do something")
   113  	s.assertAPICalls(c, "current-user", "sekrit")
   114  }
   115  
   116  func (s *ChangePasswordCommandSuite) TestChangeOthersPassword(c *gc.C) {
   117  	// The checks for user existence and admin rights are tested
   118  	// at the apiserver level.
   119  	_, _, err := s.run(c, "other")
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	s.assertAPICalls(c, "other", "sekrit")
   122  }
   123  
   124  func (s *ChangePasswordCommandSuite) TestResetSelfPasswordFail(c *gc.C) {
   125  	context, _, err := s.run(c, "--reset")
   126  	s.assertResetSelfPasswordFail(c, context, err)
   127  }
   128  
   129  func (s *ChangePasswordCommandSuite) TestResetSelfPasswordSpecifyYourselfFail(c *gc.C) {
   130  	context, _, err := s.run(c, "--reset", "current-user")
   131  	s.assertResetSelfPasswordFail(c, context, err)
   132  }
   133  
   134  func (s *ChangePasswordCommandSuite) TestResetPasswordFail(c *gc.C) {
   135  	s.mockAPI.SetErrors(errors.New("failed to do something"))
   136  	context, _, err := s.run(c, "--reset", "other")
   137  	c.Assert(err, gc.ErrorMatches, "failed to do something")
   138  	s.mockAPI.CheckCalls(c, []testing.StubCall{
   139  		{"BestAPIVersion", nil},
   140  		{"ResetPassword", []interface{}{"other"}},
   141  	})
   142  	// TODO (anastasiamac 2017-08-17)
   143  	// should probably warn user that something did not go well enough
   144  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   145  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   146  }
   147  
   148  func (s *ChangePasswordCommandSuite) TestResetOthersPassword(c *gc.C) {
   149  	// The checks for user existence and admin rights are tested
   150  	// at the apiserver level.
   151  	s.mockAPI.key = []byte("no cats or dragons")
   152  	context, _, err := s.run(c, "other", "--reset")
   153  	c.Assert(err, jc.ErrorIsNil)
   154  	s.mockAPI.CheckCalls(c, []testing.StubCall{
   155  		{"BestAPIVersion", nil},
   156  		{"ResetPassword", []interface{}{"other"}},
   157  	})
   158  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   159  	c.Assert(cmdtesting.Stderr(context), gc.Matches, `
   160  Password for "other" has been reset.
   161  Ask the user to run:
   162       juju register (.+)
   163  `[1:])
   164  }
   165  
   166  func (s *ChangePasswordCommandSuite) TestResetPasswordOldAPI(c *gc.C) {
   167  	s.mockAPI.version = 1
   168  	context, _, err := s.run(c, "--reset", "other")
   169  	c.Assert(err, gc.ErrorMatches, "on this juju controller, reset password not supported")
   170  	s.mockAPI.CheckCalls(c, []testing.StubCall{
   171  		{"BestAPIVersion", nil},
   172  	})
   173  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   174  	c.Assert(cmdtesting.Stderr(context), gc.Equals, "")
   175  }
   176  
   177  func (s *ChangePasswordCommandSuite) assertResetSelfPasswordFail(c *gc.C, context *cmd.Context, err error) {
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	s.mockAPI.CheckCalls(c, nil)
   180  	c.Assert(cmdtesting.Stdout(context), gc.Equals, "")
   181  	c.Assert(cmdtesting.Stderr(context), gc.Matches, `
   182  You cannot reset your own password.
   183  If you want to change it, please call `[1:]+"`juju change-user-password`"+` without --reset option.
   184  `)
   185  }
   186  
   187  type mockChangePasswordAPI struct {
   188  	testing.Stub
   189  	key     []byte
   190  	version int
   191  }
   192  
   193  func (m *mockChangePasswordAPI) SetPassword(username, password string) error {
   194  	m.MethodCall(m, "SetPassword", username, password)
   195  	return m.NextErr()
   196  }
   197  
   198  func (m *mockChangePasswordAPI) ResetPassword(username string) ([]byte, error) {
   199  	m.MethodCall(m, "ResetPassword", username)
   200  	return m.key, m.NextErr()
   201  }
   202  
   203  func (m *mockChangePasswordAPI) Close() error {
   204  	m.MethodCall(m, "Close")
   205  	return nil
   206  }
   207  
   208  func (m *mockChangePasswordAPI) BestAPIVersion() int {
   209  	m.MethodCall(m, "BestAPIVersion")
   210  	return m.version
   211  }
   212  
   213  type mockAPIConnection struct {
   214  	api.Connection
   215  }
   216  
   217  func (mockAPIConnection) Close() error {
   218  	return nil
   219  }