github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/keymanager/keymanager_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package keymanager_test
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	jc "github.com/juju/testing/checkers"
    11  	"github.com/juju/utils/ssh"
    12  	sshtesting "github.com/juju/utils/ssh/testing"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/names.v2"
    15  
    16  	"github.com/juju/juju/apiserver/common"
    17  	commontesting "github.com/juju/juju/apiserver/common/testing"
    18  	"github.com/juju/juju/apiserver/keymanager"
    19  	keymanagertesting "github.com/juju/juju/apiserver/keymanager/testing"
    20  	"github.com/juju/juju/apiserver/params"
    21  	apiservertesting "github.com/juju/juju/apiserver/testing"
    22  	jujutesting "github.com/juju/juju/juju/testing"
    23  )
    24  
    25  type keyManagerSuite struct {
    26  	jujutesting.JujuConnSuite
    27  
    28  	keymanager *keymanager.KeyManagerAPI
    29  	resources  *common.Resources
    30  	authoriser apiservertesting.FakeAuthorizer
    31  
    32  	commontesting.BlockHelper
    33  }
    34  
    35  var _ = gc.Suite(&keyManagerSuite{})
    36  
    37  func (s *keyManagerSuite) SetUpTest(c *gc.C) {
    38  	s.JujuConnSuite.SetUpTest(c)
    39  	s.resources = common.NewResources()
    40  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    41  
    42  	s.authoriser = apiservertesting.FakeAuthorizer{
    43  		Tag: s.AdminUserTag(c),
    44  	}
    45  	var err error
    46  	s.keymanager, err = keymanager.NewKeyManagerAPI(s.State, s.resources, s.authoriser)
    47  	c.Assert(err, jc.ErrorIsNil)
    48  
    49  	s.BlockHelper = commontesting.NewBlockHelper(s.APIState)
    50  	s.AddCleanup(func(*gc.C) { s.BlockHelper.Close() })
    51  }
    52  
    53  func (s *keyManagerSuite) TestNewKeyManagerAPIAcceptsClient(c *gc.C) {
    54  	endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, s.authoriser)
    55  	c.Assert(err, jc.ErrorIsNil)
    56  	c.Assert(endPoint, gc.NotNil)
    57  }
    58  
    59  func (s *keyManagerSuite) TestNewKeyManagerAPIAcceptsEnvironManager(c *gc.C) {
    60  	anAuthoriser := s.authoriser
    61  	anAuthoriser.EnvironManager = true
    62  	endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser)
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	c.Assert(endPoint, gc.NotNil)
    65  }
    66  
    67  func (s *keyManagerSuite) TestNewKeyManagerAPIRefusesNonClient(c *gc.C) {
    68  	anAuthoriser := s.authoriser
    69  	anAuthoriser.Tag = names.NewUnitTag("mysql/0")
    70  	anAuthoriser.EnvironManager = false
    71  	endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser)
    72  	c.Assert(endPoint, gc.IsNil)
    73  	c.Assert(err, gc.ErrorMatches, "permission denied")
    74  }
    75  
    76  func (s *keyManagerSuite) TestNewKeyManagerAPIRefusesNonEnvironManager(c *gc.C) {
    77  	anAuthoriser := s.authoriser
    78  	anAuthoriser.Tag = names.NewMachineTag("99")
    79  	anAuthoriser.EnvironManager = false
    80  	endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser)
    81  	c.Assert(endPoint, gc.IsNil)
    82  	c.Assert(err, gc.ErrorMatches, "permission denied")
    83  }
    84  
    85  func (s *keyManagerSuite) setAuthorisedKeys(c *gc.C, keys string) {
    86  	err := s.State.UpdateModelConfig(map[string]interface{}{"authorized-keys": keys}, nil, nil)
    87  	c.Assert(err, jc.ErrorIsNil)
    88  	envConfig, err := s.State.ModelConfig()
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	c.Assert(envConfig.AuthorizedKeys(), gc.Equals, keys)
    91  }
    92  
    93  func (s *keyManagerSuite) TestListKeys(c *gc.C) {
    94  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
    95  	key2 := sshtesting.ValidKeyTwo.Key
    96  	s.setAuthorisedKeys(c, strings.Join([]string{key1, key2, "bad key"}, "\n"))
    97  
    98  	args := params.ListSSHKeys{
    99  		Entities: params.Entities{[]params.Entity{
   100  			{Tag: s.AdminUserTag(c).Name()},
   101  			{Tag: "invalid"},
   102  		}},
   103  		Mode: ssh.FullKeys,
   104  	}
   105  	results, err := s.keymanager.ListKeys(args)
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	c.Assert(results, gc.DeepEquals, params.StringsResults{
   108  		Results: []params.StringsResult{
   109  			{Result: []string{key1, key2, "Invalid key: bad key"}},
   110  			{Result: []string{key1, key2, "Invalid key: bad key"}},
   111  		},
   112  	})
   113  }
   114  
   115  func (s *keyManagerSuite) TestListKeysHidesJujuInternal(c *gc.C) {
   116  	key1 := sshtesting.ValidKeyOne.Key + " juju-client-key"
   117  	key2 := sshtesting.ValidKeyTwo.Key + " juju-system-key"
   118  	s.setAuthorisedKeys(c, strings.Join([]string{key1, key2}, "\n"))
   119  
   120  	args := params.ListSSHKeys{
   121  		Entities: params.Entities{[]params.Entity{
   122  			{Tag: s.AdminUserTag(c).Name()},
   123  		}},
   124  		Mode: ssh.FullKeys,
   125  	}
   126  	results, err := s.keymanager.ListKeys(args)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	c.Assert(results, gc.DeepEquals, params.StringsResults{
   129  		Results: []params.StringsResult{
   130  			{Result: nil},
   131  		},
   132  	})
   133  }
   134  
   135  func (s *keyManagerSuite) assertEnvironKeys(c *gc.C, expected []string) {
   136  	envConfig, err := s.State.ModelConfig()
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	keys := envConfig.AuthorizedKeys()
   139  	c.Assert(keys, gc.Equals, strings.Join(expected, "\n"))
   140  }
   141  
   142  func (s *keyManagerSuite) TestAddKeys(c *gc.C) {
   143  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   144  	key2 := sshtesting.ValidKeyTwo.Key
   145  	initialKeys := []string{key1, key2, "bad key"}
   146  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   147  
   148  	newKey := sshtesting.ValidKeyThree.Key + " newuser@host"
   149  	args := params.ModifyUserSSHKeys{
   150  		User: s.AdminUserTag(c).Name(),
   151  		Keys: []string{key2, newKey, "invalid-key"},
   152  	}
   153  	results, err := s.keymanager.AddKeys(args)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	c.Assert(results, gc.DeepEquals, params.ErrorResults{
   156  		Results: []params.ErrorResult{
   157  			{Error: apiservertesting.ServerError(fmt.Sprintf("duplicate ssh key: %s", key2))},
   158  			{Error: nil},
   159  			{Error: apiservertesting.ServerError("invalid ssh key: invalid-key")},
   160  		},
   161  	})
   162  	s.assertEnvironKeys(c, append(initialKeys, newKey))
   163  }
   164  
   165  func (s *keyManagerSuite) TestBlockAddKeys(c *gc.C) {
   166  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   167  	key2 := sshtesting.ValidKeyTwo.Key
   168  	initialKeys := []string{key1, key2, "bad key"}
   169  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   170  
   171  	newKey := sshtesting.ValidKeyThree.Key + " newuser@host"
   172  	args := params.ModifyUserSSHKeys{
   173  		User: s.AdminUserTag(c).Name(),
   174  		Keys: []string{key2, newKey, "invalid-key"},
   175  	}
   176  
   177  	s.BlockAllChanges(c, "TestBlockAddKeys")
   178  	_, err := s.keymanager.AddKeys(args)
   179  	// Check that the call is blocked
   180  	s.AssertBlocked(c, err, "TestBlockAddKeys")
   181  	s.assertEnvironKeys(c, initialKeys)
   182  }
   183  
   184  func (s *keyManagerSuite) TestAddJujuSystemKey(c *gc.C) {
   185  	anAuthoriser := s.authoriser
   186  	anAuthoriser.EnvironManager = true
   187  	anAuthoriser.Tag = names.NewMachineTag("0")
   188  	var err error
   189  	s.keymanager, err = keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser)
   190  	c.Assert(err, jc.ErrorIsNil)
   191  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   192  	key2 := sshtesting.ValidKeyTwo.Key
   193  	initialKeys := []string{key1, key2}
   194  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   195  
   196  	newKey := sshtesting.ValidKeyThree.Key + " juju-system-key"
   197  	args := params.ModifyUserSSHKeys{
   198  		User: "juju-system-key",
   199  		Keys: []string{newKey},
   200  	}
   201  	results, err := s.keymanager.AddKeys(args)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	c.Assert(results, gc.DeepEquals, params.ErrorResults{
   204  		Results: []params.ErrorResult{
   205  			{Error: nil},
   206  		},
   207  	})
   208  	s.assertEnvironKeys(c, append(initialKeys, newKey))
   209  }
   210  
   211  func (s *keyManagerSuite) TestAddJujuSystemKeyNotMachine(c *gc.C) {
   212  	anAuthoriser := s.authoriser
   213  	anAuthoriser.EnvironManager = true
   214  	anAuthoriser.Tag = names.NewUnitTag("wordpress/0")
   215  	var err error
   216  	s.keymanager, err = keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser)
   217  	c.Assert(err, jc.ErrorIsNil)
   218  	key1 := sshtesting.ValidKeyOne.Key
   219  	s.setAuthorisedKeys(c, key1)
   220  
   221  	newKey := sshtesting.ValidKeyThree.Key + " juju-system-key"
   222  	args := params.ModifyUserSSHKeys{
   223  		User: "juju-system-key",
   224  		Keys: []string{newKey},
   225  	}
   226  	_, err = s.keymanager.AddKeys(args)
   227  	c.Assert(err, gc.ErrorMatches, "permission denied")
   228  	c.Assert(params.ErrCode(err), gc.Equals, params.CodeUnauthorized)
   229  	s.assertEnvironKeys(c, []string{key1})
   230  }
   231  
   232  func (s *keyManagerSuite) TestDeleteKeys(c *gc.C) {
   233  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   234  	key2 := sshtesting.ValidKeyTwo.Key
   235  	initialKeys := []string{key1, key2, "bad key"}
   236  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   237  
   238  	args := params.ModifyUserSSHKeys{
   239  		User: s.AdminUserTag(c).Name(),
   240  		Keys: []string{sshtesting.ValidKeyTwo.Fingerprint, sshtesting.ValidKeyThree.Fingerprint, "invalid-key"},
   241  	}
   242  	results, err := s.keymanager.DeleteKeys(args)
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	c.Assert(results, gc.DeepEquals, params.ErrorResults{
   245  		Results: []params.ErrorResult{
   246  			{Error: nil},
   247  			{Error: apiservertesting.ServerError("invalid ssh key: " + sshtesting.ValidKeyThree.Fingerprint)},
   248  			{Error: apiservertesting.ServerError("invalid ssh key: invalid-key")},
   249  		},
   250  	})
   251  	s.assertEnvironKeys(c, []string{key1, "bad key"})
   252  }
   253  
   254  func (s *keyManagerSuite) TestDeleteKeysNotJujuInternal(c *gc.C) {
   255  	key1 := sshtesting.ValidKeyOne.Key + " juju-client-key"
   256  	key2 := sshtesting.ValidKeyTwo.Key + " juju-system-key"
   257  	key3 := sshtesting.ValidKeyThree.Key + " a user key"
   258  	initialKeys := []string{key1, key2, key3}
   259  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   260  
   261  	args := params.ModifyUserSSHKeys{
   262  		User: s.AdminUserTag(c).Name(),
   263  		Keys: []string{"juju-client-key", "juju-system-key"},
   264  	}
   265  	results, err := s.keymanager.DeleteKeys(args)
   266  	c.Check(results, gc.DeepEquals, params.ErrorResults{
   267  		Results: []params.ErrorResult{
   268  			{Error: apiservertesting.ServerError("may not delete internal key: juju-client-key")},
   269  			{Error: apiservertesting.ServerError("may not delete internal key: juju-system-key")},
   270  		},
   271  	})
   272  	c.Assert(err, jc.ErrorIsNil)
   273  	s.assertEnvironKeys(c, initialKeys)
   274  }
   275  
   276  func (s *keyManagerSuite) TestBlockDeleteKeys(c *gc.C) {
   277  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   278  	key2 := sshtesting.ValidKeyTwo.Key
   279  	initialKeys := []string{key1, key2, "bad key"}
   280  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   281  
   282  	args := params.ModifyUserSSHKeys{
   283  		User: s.AdminUserTag(c).Name(),
   284  		Keys: []string{sshtesting.ValidKeyTwo.Fingerprint, sshtesting.ValidKeyThree.Fingerprint, "invalid-key"},
   285  	}
   286  
   287  	s.BlockAllChanges(c, "TestBlockDeleteKeys")
   288  	_, err := s.keymanager.DeleteKeys(args)
   289  	// Check that the call is blocked
   290  	s.AssertBlocked(c, err, "TestBlockDeleteKeys")
   291  	s.assertEnvironKeys(c, initialKeys)
   292  }
   293  
   294  func (s *keyManagerSuite) TestCannotDeleteAllKeys(c *gc.C) {
   295  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   296  	key2 := sshtesting.ValidKeyTwo.Key
   297  	initialKeys := []string{key1, key2}
   298  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   299  
   300  	args := params.ModifyUserSSHKeys{
   301  		User: s.AdminUserTag(c).Name(),
   302  		Keys: []string{sshtesting.ValidKeyTwo.Fingerprint, "user@host"},
   303  	}
   304  	_, err := s.keymanager.DeleteKeys(args)
   305  	c.Assert(err, gc.ErrorMatches, "cannot delete all keys")
   306  	s.assertEnvironKeys(c, initialKeys)
   307  }
   308  
   309  func (s *keyManagerSuite) assertInvalidUserOperation(c *gc.C, runTestLogic func(args params.ModifyUserSSHKeys) error) {
   310  	initialKey := sshtesting.ValidKeyOne.Key + " user@host"
   311  	s.setAuthorisedKeys(c, initialKey)
   312  
   313  	// Set up the params.
   314  	newKey := sshtesting.ValidKeyThree.Key + " newuser@host"
   315  	args := params.ModifyUserSSHKeys{
   316  		User: "invalid",
   317  		Keys: []string{newKey},
   318  	}
   319  	// Run the required test code and check the error.
   320  	err := runTestLogic(args)
   321  	c.Assert(err, gc.DeepEquals, apiservertesting.ErrUnauthorized)
   322  
   323  	// No environ changes.
   324  	s.assertEnvironKeys(c, []string{initialKey})
   325  }
   326  
   327  func (s *keyManagerSuite) TestAddKeysInvalidUser(c *gc.C) {
   328  	c.Skip("no user validation done yet")
   329  	s.assertInvalidUserOperation(c, func(args params.ModifyUserSSHKeys) error {
   330  		_, err := s.keymanager.AddKeys(args)
   331  		return err
   332  	})
   333  }
   334  
   335  func (s *keyManagerSuite) TestDeleteKeysInvalidUser(c *gc.C) {
   336  	c.Skip("no user validation done yet")
   337  	s.assertInvalidUserOperation(c, func(args params.ModifyUserSSHKeys) error {
   338  		_, err := s.keymanager.DeleteKeys(args)
   339  		return err
   340  	})
   341  }
   342  
   343  func (s *keyManagerSuite) TestImportKeys(c *gc.C) {
   344  	s.PatchValue(&keymanager.RunSSHImportId, keymanagertesting.FakeImport)
   345  
   346  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   347  	key2 := sshtesting.ValidKeyTwo.Key
   348  	key3 := sshtesting.ValidKeyThree.Key
   349  	key4 := sshtesting.ValidKeyFour.Key
   350  	keymv := strings.Split(sshtesting.ValidKeyMulti, "\n")
   351  	keymp := strings.Split(sshtesting.PartValidKeyMulti, "\n")
   352  	keymi := strings.Split(sshtesting.MultiInvalid, "\n")
   353  	initialKeys := []string{key1, key2, "bad key"}
   354  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   355  
   356  	args := params.ModifyUserSSHKeys{
   357  		User: s.AdminUserTag(c).Name(),
   358  		Keys: []string{
   359  			"lp:existing",
   360  			"lp:validuser",
   361  			"invalid-key",
   362  			"lp:multi",
   363  			"lp:multiempty",
   364  			"lp:multipartial",
   365  			"lp:multiinvalid",
   366  			"lp:multionedup",
   367  		},
   368  	}
   369  	results, err := s.keymanager.ImportKeys(args)
   370  	c.Assert(err, jc.ErrorIsNil)
   371  	c.Assert(results.Results, gc.HasLen, 8)
   372  	c.Assert(results, gc.DeepEquals, params.ErrorResults{
   373  		Results: []params.ErrorResult{
   374  			{Error: apiservertesting.ServerError(fmt.Sprintf("duplicate ssh key: %s", key2))},
   375  			{Error: nil},
   376  			{Error: apiservertesting.ServerError("invalid ssh key id: invalid-key")},
   377  			{Error: nil},
   378  			{Error: apiservertesting.ServerError("invalid ssh key id: lp:multiempty")},
   379  			{Error: apiservertesting.ServerError(fmt.Sprintf(
   380  				`invalid ssh key for lp:multipartial: `+
   381  					`generating key fingerprint: `+
   382  					`invalid authorized_key "%s"`, keymp[1]))},
   383  			{Error: apiservertesting.ServerError(fmt.Sprintf(
   384  				`invalid ssh key for lp:multiinvalid: `+
   385  					`generating key fingerprint: `+
   386  					`invalid authorized_key "%s"`+"\n"+
   387  					`invalid ssh key for lp:multiinvalid: `+
   388  					`generating key fingerprint: `+
   389  					`invalid authorized_key "%s"`, keymi[0], keymi[1]))},
   390  			{Error: apiservertesting.ServerError(fmt.Sprintf("duplicate ssh key: %s", key2))},
   391  		},
   392  	})
   393  	s.assertEnvironKeys(c, append(initialKeys, key3, keymv[0], keymv[1], keymp[0], key4))
   394  }
   395  
   396  func (s *keyManagerSuite) TestBlockImportKeys(c *gc.C) {
   397  	s.PatchValue(&keymanager.RunSSHImportId, keymanagertesting.FakeImport)
   398  
   399  	key1 := sshtesting.ValidKeyOne.Key + " user@host"
   400  	key2 := sshtesting.ValidKeyTwo.Key
   401  	initialKeys := []string{key1, key2, "bad key"}
   402  	s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n"))
   403  
   404  	args := params.ModifyUserSSHKeys{
   405  		User: s.AdminUserTag(c).Name(),
   406  		Keys: []string{"lp:existing", "lp:validuser", "invalid-key"},
   407  	}
   408  
   409  	s.BlockAllChanges(c, "TestBlockImportKeys")
   410  	_, err := s.keymanager.ImportKeys(args)
   411  	// Check that the call is blocked
   412  	s.AssertBlocked(c, err, "TestBlockImportKeys")
   413  	s.assertEnvironKeys(c, initialKeys)
   414  }