github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/daemon/api_users_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package daemon
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"net/http"
    26  	"os/user"
    27  	"path/filepath"
    28  	"time"
    29  
    30  	"gopkg.in/check.v1"
    31  
    32  	"github.com/snapcore/snapd/asserts"
    33  	"github.com/snapcore/snapd/asserts/assertstest"
    34  	"github.com/snapcore/snapd/osutil"
    35  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    36  	"github.com/snapcore/snapd/overlord/auth"
    37  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    38  	"github.com/snapcore/snapd/release"
    39  	"github.com/snapcore/snapd/store"
    40  	"github.com/snapcore/snapd/testutil"
    41  )
    42  
    43  var _ = check.Suite(&userSuite{})
    44  
    45  // TODO: also pull login/logout tests into this.
    46  // TODO: move to daemon_test package
    47  
    48  type userSuite struct {
    49  	apiBaseSuite
    50  
    51  	mockUserHome   string
    52  	restoreClassic func()
    53  	oldUserAdmin   bool
    54  }
    55  
    56  func (s *userSuite) SetUpTest(c *check.C) {
    57  	s.apiBaseSuite.SetUpTest(c)
    58  
    59  	s.restoreClassic = release.MockOnClassic(false)
    60  
    61  	s.daemon(c)
    62  	s.mockUserHome = c.MkDir()
    63  	userLookup = mkUserLookup(s.mockUserHome)
    64  	s.oldUserAdmin = hasUserAdmin
    65  	hasUserAdmin = true
    66  
    67  	// make sure we don't call these by accident
    68  	osutilAddUser = func(name string, opts *osutil.AddUserOptions) error {
    69  		c.Fatalf("unexpected add user %q call", name)
    70  		return fmt.Errorf("unexpected add user %q call", name)
    71  	}
    72  	osutilDelUser = func(name string, opts *osutil.DelUserOptions) error {
    73  		c.Fatalf("unexpected del user %q call", name)
    74  		return fmt.Errorf("unexpected del user %q call", name)
    75  	}
    76  }
    77  
    78  func (s *userSuite) TearDownTest(c *check.C) {
    79  	s.apiBaseSuite.TearDownTest(c)
    80  
    81  	userLookup = user.Lookup
    82  	osutilAddUser = osutil.AddUser
    83  	osutilDelUser = osutil.DelUser
    84  
    85  	s.restoreClassic()
    86  	hasUserAdmin = s.oldUserAdmin
    87  }
    88  
    89  func mkUserLookup(userHomeDir string) func(string) (*user.User, error) {
    90  	return func(username string) (*user.User, error) {
    91  		cur, err := user.Current()
    92  		cur.Username = username
    93  		cur.HomeDir = userHomeDir
    94  		return cur, err
    95  	}
    96  }
    97  
    98  func (s *userSuite) TestPostCreateUserNoSSHKeys(c *check.C) {
    99  	s.userInfoExpectedEmail = "popper@lse.ac.uk"
   100  	s.userInfoResult = &store.User{
   101  		Username:         "karl",
   102  		OpenIDIdentifier: "xxyyzz",
   103  	}
   104  	buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail))
   105  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   106  	c.Assert(err, check.IsNil)
   107  
   108  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   109  
   110  	c.Check(rsp.Type, check.Equals, ResponseTypeError)
   111  	c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user for "popper@lse.ac.uk": no ssh keys found`)
   112  }
   113  
   114  func (s *userSuite) TestPostCreateUser(c *check.C) {
   115  	s.testCreateUser(c, true)
   116  }
   117  
   118  func (s *userSuite) TestPostUserCreate(c *check.C) {
   119  	s.testCreateUser(c, false)
   120  }
   121  
   122  func (s *userSuite) testCreateUser(c *check.C, oldWay bool) {
   123  	expectedUsername := "karl"
   124  	s.userInfoExpectedEmail = "popper@lse.ac.uk"
   125  	s.userInfoResult = &store.User{
   126  		Username:         expectedUsername,
   127  		SSHKeys:          []string{"ssh1", "ssh2"},
   128  		OpenIDIdentifier: "xxyyzz",
   129  	}
   130  	osutilAddUser = func(username string, opts *osutil.AddUserOptions) error {
   131  		c.Check(username, check.Equals, expectedUsername)
   132  		c.Check(opts.SSHKeys, check.DeepEquals, []string{"ssh1", "ssh2"})
   133  		c.Check(opts.Gecos, check.Equals, "popper@lse.ac.uk,xxyyzz")
   134  		c.Check(opts.Sudoer, check.Equals, false)
   135  		return nil
   136  	}
   137  
   138  	var rsp *resp
   139  	var expected interface{}
   140  	expectedItem := userResponseData{
   141  		Username: expectedUsername,
   142  		SSHKeys:  []string{"ssh1", "ssh2"},
   143  	}
   144  
   145  	if oldWay {
   146  		buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail))
   147  		req, err := http.NewRequest("POST", "/v2/create-user", buf)
   148  		c.Assert(err, check.IsNil)
   149  
   150  		rsp = postCreateUser(createUserCmd, req, nil).(*resp)
   151  		expected = &expectedItem
   152  	} else {
   153  		buf := bytes.NewBufferString(fmt.Sprintf(`{"action":"create","email": "%s"}`, s.userInfoExpectedEmail))
   154  		req, err := http.NewRequest("POST", "/v2/users", buf)
   155  		c.Assert(err, check.IsNil)
   156  
   157  		rsp = postUsers(usersCmd, req, nil).(*resp)
   158  		expected = []userResponseData{expectedItem}
   159  	}
   160  
   161  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   162  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   163  	c.Check(rsp.Result, check.DeepEquals, expected)
   164  
   165  	// user was setup in state
   166  	state := s.d.overlord.State()
   167  	state.Lock()
   168  	user, err := auth.User(state, 1)
   169  	state.Unlock()
   170  	c.Check(err, check.IsNil)
   171  	c.Check(user.Username, check.Equals, expectedUsername)
   172  	c.Check(user.Email, check.Equals, s.userInfoExpectedEmail)
   173  	c.Check(user.Macaroon, check.NotNil)
   174  	// auth saved to user home dir
   175  	outfile := filepath.Join(s.mockUserHome, ".snap", "auth.json")
   176  	c.Check(osutil.FileExists(outfile), check.Equals, true)
   177  	c.Check(outfile, testutil.FileEquals,
   178  		fmt.Sprintf(`{"id":%d,"username":"%s","email":"%s","macaroon":"%s"}`,
   179  			1, expectedUsername, s.userInfoExpectedEmail, user.Macaroon))
   180  }
   181  
   182  func (s *userSuite) TestNoUserAdminCreateUser(c *check.C) { s.testNoUserAdmin(c, "/v2/create-user") }
   183  func (s *userSuite) TestNoUserAdminPostUser(c *check.C)   { s.testNoUserAdmin(c, "/v2/users") }
   184  func (s *userSuite) testNoUserAdmin(c *check.C, endpoint string) {
   185  	hasUserAdmin = false
   186  
   187  	buf := bytes.NewBufferString("{}")
   188  	req, err := http.NewRequest("POST", endpoint, buf)
   189  	c.Assert(err, check.IsNil)
   190  
   191  	switch endpoint {
   192  	case "/v2/users":
   193  		rsp := postUsers(usersCmd, req, nil).(*resp)
   194  		c.Check(rsp, check.DeepEquals, MethodNotAllowed(noUserAdmin))
   195  	case "/v2/create-user":
   196  		rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   197  		c.Check(rsp, check.DeepEquals, Forbidden(noUserAdmin))
   198  	default:
   199  		c.Fatalf("unknown endpoint %q", endpoint)
   200  	}
   201  }
   202  
   203  func (s *userSuite) TestPostUserBadBody(c *check.C) {
   204  	buf := bytes.NewBufferString(`42`)
   205  	req, err := http.NewRequest("POST", "/v2/users", buf)
   206  	c.Assert(err, check.IsNil)
   207  
   208  	rsp := postUsers(usersCmd, req, nil).(*resp)
   209  	c.Check(rsp.Type, check.Equals, ResponseTypeError)
   210  	c.Check(rsp.Result.(*errorResult).Message, check.Matches, "cannot decode user action data from request body: .*")
   211  }
   212  
   213  func (s *userSuite) TestPostUserBadAfterBody(c *check.C) {
   214  	buf := bytes.NewBufferString(`{}42`)
   215  	req, err := http.NewRequest("POST", "/v2/users", buf)
   216  	c.Assert(err, check.IsNil)
   217  
   218  	rsp := postUsers(usersCmd, req, nil).(*resp)
   219  	c.Check(rsp, check.DeepEquals, BadRequest("spurious content after user action"))
   220  }
   221  
   222  func (s *userSuite) TestPostUserNoAction(c *check.C) {
   223  	buf := bytes.NewBufferString("{}")
   224  	req, err := http.NewRequest("POST", "/v2/users", buf)
   225  	c.Assert(err, check.IsNil)
   226  
   227  	rsp := postUsers(usersCmd, req, nil).(*resp)
   228  	c.Check(rsp, check.DeepEquals, BadRequest("missing user action"))
   229  }
   230  
   231  func (s *userSuite) TestPostUserBadAction(c *check.C) {
   232  	buf := bytes.NewBufferString(`{"action":"patatas"}`)
   233  	req, err := http.NewRequest("POST", "/v2/users", buf)
   234  	c.Assert(err, check.IsNil)
   235  
   236  	rsp := postUsers(usersCmd, req, nil).(*resp)
   237  	c.Check(rsp, check.DeepEquals, BadRequest(`unsupported user action "patatas"`))
   238  }
   239  
   240  func (s *userSuite) TestPostUserActionRemoveNoUsername(c *check.C) {
   241  	buf := bytes.NewBufferString(`{"action":"remove"}`)
   242  	req, err := http.NewRequest("POST", "/v2/users", buf)
   243  	c.Assert(err, check.IsNil)
   244  
   245  	rsp := postUsers(usersCmd, req, nil).(*resp)
   246  	c.Check(rsp, check.DeepEquals, BadRequest("need a username to remove"))
   247  }
   248  
   249  func (s *userSuite) TestPostUserActionRemoveDelUserErr(c *check.C) {
   250  	st := s.d.overlord.State()
   251  	st.Lock()
   252  	_, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"})
   253  	st.Unlock()
   254  	c.Check(err, check.IsNil)
   255  
   256  	called := 0
   257  	osutilDelUser = func(username string, opts *osutil.DelUserOptions) error {
   258  		called++
   259  		c.Check(username, check.Equals, "some-user")
   260  		return fmt.Errorf("wat")
   261  	}
   262  
   263  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   264  	req, err := http.NewRequest("POST", "/v2/users", buf)
   265  	c.Assert(err, check.IsNil)
   266  
   267  	rsp := postUsers(usersCmd, req, nil).(*resp)
   268  	c.Check(rsp.Status, check.Equals, 500)
   269  	c.Check(rsp.Result.(*errorResult).Message, check.Equals, "wat")
   270  	c.Check(called, check.Equals, 1)
   271  }
   272  
   273  func (s *userSuite) TestPostUserActionRemoveStateErr(c *check.C) {
   274  	st := s.d.overlord.State()
   275  	st.Lock()
   276  	st.Set("auth", 42) // breaks auth
   277  	st.Unlock()
   278  	called := 0
   279  	osutilDelUser = func(username string, opts *osutil.DelUserOptions) error {
   280  		called++
   281  		c.Check(username, check.Equals, "some-user")
   282  		return nil
   283  	}
   284  
   285  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   286  	req, err := http.NewRequest("POST", "/v2/users", buf)
   287  	c.Assert(err, check.IsNil)
   288  
   289  	rsp := postUsers(usersCmd, req, nil).(*resp)
   290  	c.Check(rsp.Status, check.Equals, 500)
   291  	c.Check(rsp.Result.(*errorResult).Message, check.Matches, `internal error: could not unmarshal state entry "auth": .*`)
   292  	c.Check(called, check.Equals, 0)
   293  }
   294  
   295  func (s *userSuite) TestPostUserActionRemoveNoUserInState(c *check.C) {
   296  	called := 0
   297  	osutilDelUser = func(username string, opts *osutil.DelUserOptions) error {
   298  		called++
   299  		c.Check(username, check.Equals, "some-user")
   300  		return nil
   301  	}
   302  
   303  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   304  	req, err := http.NewRequest("POST", "/v2/users", buf)
   305  	c.Assert(err, check.IsNil)
   306  
   307  	rsp := postUsers(usersCmd, req, nil).(*resp)
   308  	c.Check(rsp, check.DeepEquals, BadRequest(`user "some-user" is not known`))
   309  	c.Check(called, check.Equals, 0)
   310  }
   311  
   312  func (s *userSuite) TestPostUserActionRemove(c *check.C) {
   313  	st := s.d.overlord.State()
   314  	st.Lock()
   315  	user, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"})
   316  	st.Unlock()
   317  	c.Check(err, check.IsNil)
   318  
   319  	called := 0
   320  	osutilDelUser = func(username string, opts *osutil.DelUserOptions) error {
   321  		called++
   322  		c.Check(username, check.Equals, "some-user")
   323  		return nil
   324  	}
   325  
   326  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   327  	req, err := http.NewRequest("POST", "/v2/users", buf)
   328  	c.Assert(err, check.IsNil)
   329  	rsp := postUsers(usersCmd, req, nil).(*resp)
   330  	c.Check(rsp.Status, check.Equals, 200)
   331  	expected := []userResponseData{
   332  		{ID: user.ID, Username: user.Username, Email: user.Email},
   333  	}
   334  	c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{
   335  		"removed": expected,
   336  	})
   337  	c.Check(called, check.Equals, 1)
   338  
   339  	// and the user is removed from state
   340  	st.Lock()
   341  	_, err = auth.User(st, user.ID)
   342  	st.Unlock()
   343  	c.Check(err, check.Equals, auth.ErrInvalidUser)
   344  }
   345  
   346  func (s *userSuite) setupSigner(accountID string, signerPrivKey asserts.PrivateKey) *assertstest.SigningDB {
   347  	st := s.d.overlord.State()
   348  
   349  	signerSigning := s.brands.Register(accountID, signerPrivKey, map[string]interface{}{
   350  		"account-id":   accountID,
   351  		"verification": "verified",
   352  	})
   353  	acctNKey := s.brands.AccountsAndKeys(accountID)
   354  
   355  	assertstest.AddMany(s.storeSigning, acctNKey...)
   356  	assertstatetest.AddMany(st, acctNKey...)
   357  
   358  	return signerSigning
   359  }
   360  
   361  var (
   362  	brandPrivKey, _   = assertstest.GenerateKey(752)
   363  	partnerPrivKey, _ = assertstest.GenerateKey(752)
   364  	unknownPrivKey, _ = assertstest.GenerateKey(752)
   365  )
   366  
   367  func (s *userSuite) makeSystemUsers(c *check.C, systemUsers []map[string]interface{}) {
   368  	st := s.d.overlord.State()
   369  	st.Lock()
   370  	defer st.Unlock()
   371  
   372  	assertstatetest.AddMany(st, s.storeSigning.StoreAccountKey(""))
   373  
   374  	s.setupSigner("my-brand", brandPrivKey)
   375  	s.setupSigner("partner", partnerPrivKey)
   376  	s.setupSigner("unknown", unknownPrivKey)
   377  
   378  	model := s.brands.Model("my-brand", "my-model", map[string]interface{}{
   379  		"architecture":          "amd64",
   380  		"gadget":                "pc",
   381  		"kernel":                "pc-kernel",
   382  		"required-snaps":        []interface{}{"required-snap1"},
   383  		"system-user-authority": []interface{}{"my-brand", "partner"},
   384  	})
   385  	// now add model related stuff to the system
   386  	assertstatetest.AddMany(st, model)
   387  	// and a serial
   388  	deviceKey, _ := assertstest.GenerateKey(752)
   389  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
   390  	c.Assert(err, check.IsNil)
   391  	serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
   392  		"authority-id":        "my-brand",
   393  		"brand-id":            "my-brand",
   394  		"model":               "my-model",
   395  		"serial":              "serialserial",
   396  		"device-key":          string(encDevKey),
   397  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
   398  		"timestamp":           time.Now().Format(time.RFC3339),
   399  	}, nil, "")
   400  	c.Assert(err, check.IsNil)
   401  	assertstatetest.AddMany(st, serial)
   402  
   403  	for _, suMap := range systemUsers {
   404  		su, err := s.brands.Signing(suMap["authority-id"].(string)).Sign(asserts.SystemUserType, suMap, nil, "")
   405  		c.Assert(err, check.IsNil)
   406  		su = su.(*asserts.SystemUser)
   407  		// now add system-user assertion to the system
   408  		assertstatetest.AddMany(st, su)
   409  	}
   410  	// create fake device
   411  	err = devicestatetest.SetDevice(st, &auth.DeviceState{
   412  		Brand:  "my-brand",
   413  		Model:  "my-model",
   414  		Serial: "serialserial",
   415  	})
   416  	c.Assert(err, check.IsNil)
   417  }
   418  
   419  var goodUser = map[string]interface{}{
   420  	"authority-id": "my-brand",
   421  	"brand-id":     "my-brand",
   422  	"email":        "foo@bar.com",
   423  	"series":       []interface{}{"16", "18"},
   424  	"models":       []interface{}{"my-model", "other-model"},
   425  	"name":         "Boring Guy",
   426  	"username":     "guy",
   427  	"password":     "$6$salt$hash",
   428  	"since":        time.Now().Format(time.RFC3339),
   429  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   430  }
   431  
   432  var partnerUser = map[string]interface{}{
   433  	"authority-id": "partner",
   434  	"brand-id":     "my-brand",
   435  	"email":        "p@partner.com",
   436  	"series":       []interface{}{"16", "18"},
   437  	"models":       []interface{}{"my-model"},
   438  	"name":         "Partner Guy",
   439  	"username":     "partnerguy",
   440  	"password":     "$6$salt$hash",
   441  	"since":        time.Now().Format(time.RFC3339),
   442  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   443  }
   444  
   445  var serialUser = map[string]interface{}{
   446  	"format":       "1",
   447  	"authority-id": "my-brand",
   448  	"brand-id":     "my-brand",
   449  	"email":        "serial@bar.com",
   450  	"series":       []interface{}{"16", "18"},
   451  	"models":       []interface{}{"my-model"},
   452  	"serials":      []interface{}{"serialserial"},
   453  	"name":         "Serial Guy",
   454  	"username":     "goodserialguy",
   455  	"password":     "$6$salt$hash",
   456  	"since":        time.Now().Format(time.RFC3339),
   457  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   458  }
   459  
   460  var badUser = map[string]interface{}{
   461  	// bad user (not valid for this model)
   462  	"authority-id": "my-brand",
   463  	"brand-id":     "my-brand",
   464  	"email":        "foobar@bar.com",
   465  	"series":       []interface{}{"16", "18"},
   466  	"models":       []interface{}{"non-of-the-models-i-have"},
   467  	"name":         "Random Gal",
   468  	"username":     "gal",
   469  	"password":     "$6$salt$hash",
   470  	"since":        time.Now().Format(time.RFC3339),
   471  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   472  }
   473  
   474  var badUserNoMatchingSerial = map[string]interface{}{
   475  	"format":       "1",
   476  	"authority-id": "my-brand",
   477  	"brand-id":     "my-brand",
   478  	"email":        "noserial@bar.com",
   479  	"series":       []interface{}{"16", "18"},
   480  	"models":       []interface{}{"my-model"},
   481  	"serials":      []interface{}{"different-serialserial"},
   482  	"name":         "No Serial Guy",
   483  	"username":     "noserial",
   484  	"password":     "$6$salt$hash",
   485  	"since":        time.Now().Format(time.RFC3339),
   486  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   487  }
   488  
   489  var unknownUser = map[string]interface{}{
   490  	"authority-id": "unknown",
   491  	"brand-id":     "my-brand",
   492  	"email":        "x@partner.com",
   493  	"series":       []interface{}{"16", "18"},
   494  	"models":       []interface{}{"my-model"},
   495  	"name":         "XGuy",
   496  	"username":     "xguy",
   497  	"password":     "$6$salt$hash",
   498  	"since":        time.Now().Format(time.RFC3339),
   499  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   500  }
   501  
   502  func (s *userSuite) TestGetUserDetailsFromAssertionHappy(c *check.C) {
   503  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   504  
   505  	st := s.d.overlord.State()
   506  
   507  	st.Lock()
   508  	model, err := s.d.overlord.DeviceManager().Model()
   509  	st.Unlock()
   510  	c.Assert(err, check.IsNil)
   511  
   512  	// ensure that if we query the details from the assert DB we get
   513  	// the expected user
   514  	username, opts, err := getUserDetailsFromAssertion(st, model, nil, "foo@bar.com")
   515  	c.Check(username, check.Equals, "guy")
   516  	c.Check(opts, check.DeepEquals, &osutil.AddUserOptions{
   517  		Gecos:    "foo@bar.com,Boring Guy",
   518  		Password: "$6$salt$hash",
   519  	})
   520  	c.Check(err, check.IsNil)
   521  }
   522  
   523  // FIXME: These tests all look similar, with small deltas. Would be
   524  // nice to transform them into a table that is just the deltas, and
   525  // run on a loop.
   526  func (s *userSuite) TestPostCreateUserFromAssertion(c *check.C) {
   527  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   528  
   529  	// mock the calls that create the user
   530  	osutilAddUser = func(username string, opts *osutil.AddUserOptions) error {
   531  		c.Check(username, check.Equals, "guy")
   532  		c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
   533  		c.Check(opts.Sudoer, check.Equals, false)
   534  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
   535  		c.Check(opts.ForcePasswordChange, check.Equals, false)
   536  		return nil
   537  	}
   538  
   539  	defer func() {
   540  		osutilAddUser = osutil.AddUser
   541  	}()
   542  
   543  	// do it!
   544  	buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`)
   545  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   546  	c.Assert(err, check.IsNil)
   547  
   548  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   549  
   550  	expected := &userResponseData{
   551  		Username: "guy",
   552  	}
   553  
   554  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   555  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   556  	c.Check(rsp.Result, check.DeepEquals, expected)
   557  
   558  	// ensure the user was added to the state
   559  	st := s.d.overlord.State()
   560  	st.Lock()
   561  	users, err := auth.Users(st)
   562  	c.Assert(err, check.IsNil)
   563  	st.Unlock()
   564  	c.Check(users, check.HasLen, 1)
   565  }
   566  
   567  func (s *userSuite) TestPostCreateUserFromAssertionWithForcePasswordChange(c *check.C) {
   568  	user := make(map[string]interface{})
   569  	for k, v := range goodUser {
   570  		user[k] = v
   571  	}
   572  	user["force-password-change"] = "true"
   573  	lusers := []map[string]interface{}{user}
   574  	s.makeSystemUsers(c, lusers)
   575  
   576  	// mock the calls that create the user
   577  	osutilAddUser = func(username string, opts *osutil.AddUserOptions) error {
   578  		c.Check(username, check.Equals, "guy")
   579  		c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
   580  		c.Check(opts.Sudoer, check.Equals, false)
   581  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
   582  		c.Check(opts.ForcePasswordChange, check.Equals, true)
   583  		return nil
   584  	}
   585  
   586  	defer func() {
   587  		osutilAddUser = osutil.AddUser
   588  	}()
   589  
   590  	// do it!
   591  	buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`)
   592  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   593  	c.Assert(err, check.IsNil)
   594  
   595  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   596  
   597  	expected := &userResponseData{
   598  		Username: "guy",
   599  	}
   600  
   601  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   602  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   603  	c.Check(rsp.Result, check.DeepEquals, expected)
   604  
   605  	// ensure the user was added to the state
   606  	st := s.d.overlord.State()
   607  	st.Lock()
   608  	users, err := auth.Users(st)
   609  	c.Assert(err, check.IsNil)
   610  	st.Unlock()
   611  	c.Check(users, check.HasLen, 1)
   612  }
   613  
   614  func (s *userSuite) TestPostCreateUserFromAssertionAllKnown(c *check.C) {
   615  	s.makeSystemUsers(c, []map[string]interface{}{goodUser, partnerUser, serialUser, badUser, badUserNoMatchingSerial, unknownUser})
   616  	created := map[string]bool{}
   617  	// mock the calls that create the user
   618  	osutilAddUser = func(username string, opts *osutil.AddUserOptions) error {
   619  		switch username {
   620  		case "guy":
   621  			c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
   622  		case "partnerguy":
   623  			c.Check(opts.Gecos, check.Equals, "p@partner.com,Partner Guy")
   624  		case "goodserialguy":
   625  			c.Check(opts.Gecos, check.Equals, "serial@bar.com,Serial Guy")
   626  		default:
   627  			c.Logf("unexpected username %q", username)
   628  			c.Fail()
   629  		}
   630  		c.Check(opts.Sudoer, check.Equals, false)
   631  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
   632  		created[username] = true
   633  		return nil
   634  	}
   635  	oldLookup := userLookup
   636  	// make sure we report them as non-existing until created
   637  	userLookup = func(username string) (*user.User, error) {
   638  		if created[username] {
   639  			return oldLookup(username)
   640  		}
   641  		return nil, fmt.Errorf("not created yet")
   642  	}
   643  
   644  	// do it!
   645  	buf := bytes.NewBufferString(`{"known":true}`)
   646  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   647  	c.Assert(err, check.IsNil)
   648  
   649  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   650  
   651  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   652  	// note that we get a list here instead of a single
   653  	// userResponseData item
   654  	c.Check(rsp.Result, check.FitsTypeOf, []userResponseData{})
   655  	seen := map[string]bool{}
   656  	for _, u := range rsp.Result.([]userResponseData) {
   657  		seen[u.Username] = true
   658  		c.Check(u, check.DeepEquals, userResponseData{Username: u.Username})
   659  	}
   660  	c.Check(seen, check.DeepEquals, map[string]bool{
   661  		"guy":           true,
   662  		"partnerguy":    true,
   663  		"goodserialguy": true,
   664  	})
   665  
   666  	// ensure the user was added to the state
   667  	st := s.d.overlord.State()
   668  	st.Lock()
   669  	users, err := auth.Users(st)
   670  	c.Assert(err, check.IsNil)
   671  	st.Unlock()
   672  	c.Check(users, check.HasLen, 3)
   673  }
   674  
   675  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownClassicErrors(c *check.C) {
   676  	restore := release.MockOnClassic(true)
   677  	defer restore()
   678  
   679  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   680  
   681  	// do it!
   682  	buf := bytes.NewBufferString(`{"known":true}`)
   683  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   684  	c.Assert(err, check.IsNil)
   685  
   686  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   687  
   688  	c.Check(rsp.Type, check.Equals, ResponseTypeError)
   689  	c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user: device is a classic system`)
   690  }
   691  
   692  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwnedErrors(c *check.C) {
   693  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   694  
   695  	st := s.d.overlord.State()
   696  	st.Lock()
   697  	_, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"})
   698  	st.Unlock()
   699  	c.Check(err, check.IsNil)
   700  
   701  	// do it!
   702  	buf := bytes.NewBufferString(`{"known":true}`)
   703  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   704  	c.Assert(err, check.IsNil)
   705  
   706  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   707  
   708  	c.Check(rsp.Type, check.Equals, ResponseTypeError)
   709  	c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user: device already managed`)
   710  }
   711  
   712  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownNoModelError(c *check.C) {
   713  	restore := release.MockOnClassic(false)
   714  	defer restore()
   715  
   716  	st := s.d.overlord.State()
   717  	// have not model yet
   718  	st.Lock()
   719  	err := devicestatetest.SetDevice(st, &auth.DeviceState{})
   720  	st.Unlock()
   721  	c.Assert(err, check.IsNil)
   722  
   723  	// do it!
   724  	buf := bytes.NewBufferString(`{"known":true}`)
   725  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   726  	c.Assert(err, check.IsNil)
   727  
   728  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   729  
   730  	c.Check(rsp.Type, check.Equals, ResponseTypeError)
   731  	c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot create user: cannot get model assertion: no state entry for key`)
   732  }
   733  
   734  func (s *userSuite) TestPostCreateUserFromAssertionNoModel(c *check.C) {
   735  	restore := release.MockOnClassic(false)
   736  	defer restore()
   737  
   738  	model := s.brands.Model("my-brand", "other-model", map[string]interface{}{
   739  		"architecture":          "amd64",
   740  		"gadget":                "pc",
   741  		"kernel":                "pc-kernel",
   742  		"system-user-authority": []interface{}{"my-brand", "partner"},
   743  	})
   744  	s.makeSystemUsers(c, []map[string]interface{}{serialUser})
   745  
   746  	st := s.d.overlord.State()
   747  	st.Lock()
   748  	assertstatetest.AddMany(st, model)
   749  	err := devicestatetest.SetDevice(st, &auth.DeviceState{
   750  		Brand:  "my-brand",
   751  		Model:  "my-model",
   752  		Serial: "other-serial-assertion",
   753  	})
   754  	st.Unlock()
   755  	c.Assert(err, check.IsNil)
   756  
   757  	// do it!
   758  	buf := bytes.NewBufferString(`{"email":"serial@bar.com", "known":true}`)
   759  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   760  	c.Assert(err, check.IsNil)
   761  
   762  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   763  
   764  	c.Check(rsp.Type, check.Equals, ResponseTypeError)
   765  	c.Check(rsp.Result.(*errorResult).Message, check.Matches, `cannot add system-user "serial@bar.com": bound to serial assertion but device not yet registered`)
   766  }
   767  
   768  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwned(c *check.C) {
   769  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   770  
   771  	st := s.d.overlord.State()
   772  	st.Lock()
   773  	_, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"})
   774  	st.Unlock()
   775  	c.Check(err, check.IsNil)
   776  
   777  	// mock the calls that create the user
   778  	created := map[string]bool{}
   779  	osutilAddUser = func(username string, opts *osutil.AddUserOptions) error {
   780  		c.Check(username, check.Equals, "guy")
   781  		c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
   782  		c.Check(opts.Sudoer, check.Equals, false)
   783  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
   784  		created[username] = true
   785  		return nil
   786  	}
   787  	oldLookup := userLookup
   788  	// make sure we report them as non-existing until created
   789  	userLookup = func(username string) (*user.User, error) {
   790  		if created[username] {
   791  			return oldLookup(username)
   792  		}
   793  		return nil, fmt.Errorf("not created yet")
   794  	}
   795  
   796  	// do it!
   797  	buf := bytes.NewBufferString(`{"known":true,"force-managed":true}`)
   798  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   799  	c.Assert(err, check.IsNil)
   800  
   801  	rsp := postCreateUser(createUserCmd, req, nil).(*resp)
   802  
   803  	// note that we get a list here instead of a single
   804  	// userResponseData item
   805  	expected := []userResponseData{
   806  		{Username: "guy"},
   807  	}
   808  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   809  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   810  	c.Check(rsp.Result, check.DeepEquals, expected)
   811  }
   812  
   813  func (s *userSuite) TestUsersEmpty(c *check.C) {
   814  	req, err := http.NewRequest("GET", "/v2/users", nil)
   815  	c.Assert(err, check.IsNil)
   816  
   817  	rsp := getUsers(usersCmd, req, nil).(*resp)
   818  
   819  	expected := []userResponseData{}
   820  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   821  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   822  	c.Check(rsp.Result, check.DeepEquals, expected)
   823  }
   824  
   825  func (s *userSuite) TestUsersHasUser(c *check.C) {
   826  	st := s.d.overlord.State()
   827  	st.Lock()
   828  	u, err := auth.NewUser(st, "someuser", "mymail@test.com", "macaroon", []string{"discharge"})
   829  	st.Unlock()
   830  	c.Assert(err, check.IsNil)
   831  
   832  	req, err := http.NewRequest("GET", "/v2/users", nil)
   833  	c.Assert(err, check.IsNil)
   834  
   835  	rsp := getUsers(usersCmd, req, nil).(*resp)
   836  
   837  	expected := []userResponseData{
   838  		{ID: u.ID, Username: u.Username, Email: u.Email},
   839  	}
   840  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   841  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   842  	c.Check(rsp.Result, check.DeepEquals, expected)
   843  }
   844  
   845  func (s *userSuite) TestSysInfoIsManaged(c *check.C) {
   846  	st := s.d.overlord.State()
   847  	st.Lock()
   848  	_, err := auth.NewUser(st, "someuser", "mymail@test.com", "macaroon", []string{"discharge"})
   849  	st.Unlock()
   850  	c.Assert(err, check.IsNil)
   851  
   852  	req, err := http.NewRequest("GET", "/v2/system-info", nil)
   853  	c.Assert(err, check.IsNil)
   854  
   855  	rsp := sysInfo(sysInfoCmd, req, nil).(*resp)
   856  
   857  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   858  	c.Check(rsp.Result.(map[string]interface{})["managed"], check.Equals, true)
   859  }
   860  
   861  func (s *userSuite) TestSysInfoWorksDegraded(c *check.C) {
   862  	s.d.SetDegradedMode(fmt.Errorf("some error"))
   863  
   864  	req, err := http.NewRequest("GET", "/v2/system-info", nil)
   865  	c.Assert(err, check.IsNil)
   866  
   867  	rsp := sysInfo(sysInfoCmd, req, nil).(*resp)
   868  	c.Check(rsp.Status, check.Equals, 200)
   869  }