github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/daemon/api_users_test.go (about)

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