github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/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  	"strings"
    29  	"time"
    30  
    31  	"gopkg.in/check.v1"
    32  
    33  	"github.com/snapcore/snapd/asserts"
    34  	"github.com/snapcore/snapd/asserts/assertstest"
    35  	"github.com/snapcore/snapd/client"
    36  	"github.com/snapcore/snapd/daemon"
    37  	"github.com/snapcore/snapd/osutil"
    38  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    39  	"github.com/snapcore/snapd/overlord/auth"
    40  	"github.com/snapcore/snapd/overlord/configstate/config"
    41  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    42  	"github.com/snapcore/snapd/release"
    43  	"github.com/snapcore/snapd/store"
    44  	"github.com/snapcore/snapd/testutil"
    45  )
    46  
    47  var _ = check.Suite(&userSuite{})
    48  
    49  type userSuite struct {
    50  	apiBaseSuite
    51  
    52  	userInfoResult        *store.User
    53  	userInfoExpectedEmail string
    54  
    55  	loginUserStoreMacaroon string
    56  	loginUserDischarge     string
    57  
    58  	mockUserHome      string
    59  	trivialUserLookup func(username string) (*user.User, error)
    60  }
    61  
    62  func (s *userSuite) UserInfo(email string) (userinfo *store.User, err error) {
    63  	s.pokeStateLock()
    64  
    65  	if s.userInfoExpectedEmail != email {
    66  		panic(fmt.Sprintf("%q != %q", s.userInfoExpectedEmail, email))
    67  	}
    68  	return s.userInfoResult, s.err
    69  }
    70  
    71  func (s *userSuite) LoginUser(username, password, otp string) (string, string, error) {
    72  	s.pokeStateLock()
    73  
    74  	return s.loginUserStoreMacaroon, s.loginUserDischarge, s.err
    75  }
    76  
    77  func (s *userSuite) SetUpTest(c *check.C) {
    78  	s.apiBaseSuite.SetUpTest(c)
    79  
    80  	s.AddCleanup(release.MockOnClassic(false))
    81  
    82  	s.daemonWithStore(c, s)
    83  
    84  	s.mockUserHome = c.MkDir()
    85  	s.trivialUserLookup = mkUserLookup(s.mockUserHome)
    86  	s.AddCleanup(daemon.MockUserLookup(s.trivialUserLookup))
    87  
    88  	s.AddCleanup(daemon.MockHasUserAdmin(true))
    89  
    90  	// make sure we don't call these by accident
    91  	s.AddCleanup(daemon.MockOsutilAddUser(func(name string, opts *osutil.AddUserOptions) error {
    92  		c.Fatalf("unexpected add user %q call", name)
    93  		return fmt.Errorf("unexpected add user %q call", name)
    94  	}))
    95  	s.AddCleanup(daemon.MockOsutilDelUser(func(name string, opts *osutil.DelUserOptions) error {
    96  		c.Fatalf("unexpected del user %q call", name)
    97  		return fmt.Errorf("unexpected del user %q call", name)
    98  	}))
    99  
   100  	s.userInfoResult = nil
   101  	s.userInfoExpectedEmail = ""
   102  
   103  	s.loginUserStoreMacaroon = ""
   104  	s.loginUserDischarge = ""
   105  }
   106  
   107  func mkUserLookup(userHomeDir string) func(string) (*user.User, error) {
   108  	return func(username string) (*user.User, error) {
   109  		cur, err := user.Current()
   110  		cur.Username = username
   111  		cur.HomeDir = userHomeDir
   112  		return cur, err
   113  	}
   114  }
   115  
   116  func (s *userSuite) TestLoginUser(c *check.C) {
   117  	state := s.d.Overlord().State()
   118  
   119  	s.loginUserStoreMacaroon = "user-macaroon"
   120  	s.loginUserDischarge = "the-discharge-macaroon-serialized-data"
   121  	buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`)
   122  	req, err := http.NewRequest("POST", "/v2/login", buf)
   123  	c.Assert(err, check.IsNil)
   124  
   125  	rsp := s.syncReq(c, req, nil)
   126  
   127  	state.Lock()
   128  	user, err := auth.User(state, 1)
   129  	state.Unlock()
   130  	c.Check(err, check.IsNil)
   131  
   132  	expected := daemon.UserResponseData{
   133  		ID:    1,
   134  		Email: "email@.com",
   135  
   136  		Macaroon:   user.Macaroon,
   137  		Discharges: user.Discharges,
   138  	}
   139  
   140  	c.Check(rsp.Status, check.Equals, 200)
   141  	c.Assert(rsp.Result, check.FitsTypeOf, expected)
   142  	c.Check(rsp.Result, check.DeepEquals, expected)
   143  
   144  	c.Check(user.ID, check.Equals, 1)
   145  	c.Check(user.Username, check.Equals, "")
   146  	c.Check(user.Email, check.Equals, "email@.com")
   147  	c.Check(user.Discharges, check.IsNil)
   148  	c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon)
   149  	c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"})
   150  	// snapd macaroon was setup too
   151  	snapdMacaroon, err := auth.MacaroonDeserialize(user.Macaroon)
   152  	c.Check(err, check.IsNil)
   153  	c.Check(snapdMacaroon.Id(), check.Equals, "1")
   154  	c.Check(snapdMacaroon.Location(), check.Equals, "snapd")
   155  }
   156  
   157  func (s *userSuite) TestLoginUserWithUsername(c *check.C) {
   158  	state := s.d.Overlord().State()
   159  
   160  	s.loginUserStoreMacaroon = "user-macaroon"
   161  	s.loginUserDischarge = "the-discharge-macaroon-serialized-data"
   162  	buf := bytes.NewBufferString(`{"username": "username", "email": "email@.com", "password": "password"}`)
   163  	req, err := http.NewRequest("POST", "/v2/login", buf)
   164  	c.Assert(err, check.IsNil)
   165  
   166  	rsp := s.syncReq(c, req, nil)
   167  
   168  	state.Lock()
   169  	user, err := auth.User(state, 1)
   170  	state.Unlock()
   171  	c.Check(err, check.IsNil)
   172  
   173  	expected := daemon.UserResponseData{
   174  		ID:         1,
   175  		Username:   "username",
   176  		Email:      "email@.com",
   177  		Macaroon:   user.Macaroon,
   178  		Discharges: user.Discharges,
   179  	}
   180  	c.Check(rsp.Status, check.Equals, 200)
   181  	c.Assert(rsp.Result, check.FitsTypeOf, expected)
   182  	c.Check(rsp.Result, check.DeepEquals, expected)
   183  
   184  	c.Check(user.ID, check.Equals, 1)
   185  	c.Check(user.Username, check.Equals, "username")
   186  	c.Check(user.Email, check.Equals, "email@.com")
   187  	c.Check(user.Discharges, check.IsNil)
   188  	c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon)
   189  	c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"})
   190  	// snapd macaroon was setup too
   191  	snapdMacaroon, err := auth.MacaroonDeserialize(user.Macaroon)
   192  	c.Check(err, check.IsNil)
   193  	c.Check(snapdMacaroon.Id(), check.Equals, "1")
   194  	c.Check(snapdMacaroon.Location(), check.Equals, "snapd")
   195  }
   196  
   197  func (s *userSuite) TestLoginUserNoEmailWithExistentLocalUser(c *check.C) {
   198  	state := s.d.Overlord().State()
   199  
   200  	// setup local-only user
   201  	state.Lock()
   202  	localUser, err := auth.NewUser(state, "username", "email@test.com", "", nil)
   203  	state.Unlock()
   204  	c.Assert(err, check.IsNil)
   205  
   206  	s.loginUserStoreMacaroon = "user-macaroon"
   207  	s.loginUserDischarge = "the-discharge-macaroon-serialized-data"
   208  	buf := bytes.NewBufferString(`{"username": "username", "email": "", "password": "password"}`)
   209  	req, err := http.NewRequest("POST", "/v2/login", buf)
   210  	c.Assert(err, check.IsNil)
   211  	req.Header.Set("Authorization", fmt.Sprintf(`Macaroon root="%s"`, localUser.Macaroon))
   212  
   213  	rsp := s.syncReq(c, req, localUser)
   214  
   215  	expected := daemon.UserResponseData{
   216  		ID:       1,
   217  		Username: "username",
   218  		Email:    "email@test.com",
   219  
   220  		Macaroon:   localUser.Macaroon,
   221  		Discharges: localUser.Discharges,
   222  	}
   223  	c.Check(rsp.Status, check.Equals, 200)
   224  	c.Assert(rsp.Result, check.FitsTypeOf, expected)
   225  	c.Check(rsp.Result, check.DeepEquals, expected)
   226  
   227  	state.Lock()
   228  	user, err := auth.User(state, localUser.ID)
   229  	state.Unlock()
   230  	c.Check(err, check.IsNil)
   231  	c.Check(user.Username, check.Equals, "username")
   232  	c.Check(user.Email, check.Equals, localUser.Email)
   233  	c.Check(user.Macaroon, check.Equals, localUser.Macaroon)
   234  	c.Check(user.Discharges, check.IsNil)
   235  	c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon)
   236  	c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"})
   237  }
   238  
   239  func (s *userSuite) TestLoginUserWithExistentLocalUser(c *check.C) {
   240  	state := s.d.Overlord().State()
   241  
   242  	// setup local-only user
   243  	state.Lock()
   244  	localUser, err := auth.NewUser(state, "username", "email@test.com", "", nil)
   245  	state.Unlock()
   246  	c.Assert(err, check.IsNil)
   247  
   248  	s.loginUserStoreMacaroon = "user-macaroon"
   249  	s.loginUserDischarge = "the-discharge-macaroon-serialized-data"
   250  	buf := bytes.NewBufferString(`{"username": "username", "email": "email@test.com", "password": "password"}`)
   251  	req, err := http.NewRequest("POST", "/v2/login", buf)
   252  	c.Assert(err, check.IsNil)
   253  	req.Header.Set("Authorization", fmt.Sprintf(`Macaroon root="%s"`, localUser.Macaroon))
   254  
   255  	rsp := s.syncReq(c, req, localUser)
   256  
   257  	expected := daemon.UserResponseData{
   258  		ID:       1,
   259  		Username: "username",
   260  		Email:    "email@test.com",
   261  
   262  		Macaroon:   localUser.Macaroon,
   263  		Discharges: localUser.Discharges,
   264  	}
   265  	c.Check(rsp.Status, check.Equals, 200)
   266  	c.Assert(rsp.Result, check.FitsTypeOf, expected)
   267  	c.Check(rsp.Result, check.DeepEquals, expected)
   268  
   269  	state.Lock()
   270  	user, err := auth.User(state, localUser.ID)
   271  	state.Unlock()
   272  	c.Check(err, check.IsNil)
   273  	c.Check(user.Username, check.Equals, "username")
   274  	c.Check(user.Email, check.Equals, localUser.Email)
   275  	c.Check(user.Macaroon, check.Equals, localUser.Macaroon)
   276  	c.Check(user.Discharges, check.IsNil)
   277  	c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon)
   278  	c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"})
   279  }
   280  
   281  func (s *userSuite) TestLoginUserNewEmailWithExistentLocalUser(c *check.C) {
   282  	state := s.d.Overlord().State()
   283  
   284  	// setup local-only user
   285  	state.Lock()
   286  	localUser, err := auth.NewUser(state, "username", "email@test.com", "", nil)
   287  	state.Unlock()
   288  	c.Assert(err, check.IsNil)
   289  
   290  	s.loginUserStoreMacaroon = "user-macaroon"
   291  	s.loginUserDischarge = "the-discharge-macaroon-serialized-data"
   292  	// same local user, but using a new SSO account
   293  	buf := bytes.NewBufferString(`{"username": "username", "email": "new.email@test.com", "password": "password"}`)
   294  	req, err := http.NewRequest("POST", "/v2/login", buf)
   295  	c.Assert(err, check.IsNil)
   296  	req.Header.Set("Authorization", fmt.Sprintf(`Macaroon root="%s"`, localUser.Macaroon))
   297  
   298  	rsp := s.syncReq(c, req, localUser)
   299  
   300  	expected := daemon.UserResponseData{
   301  		ID:       1,
   302  		Username: "username",
   303  		Email:    "new.email@test.com",
   304  
   305  		Macaroon:   localUser.Macaroon,
   306  		Discharges: localUser.Discharges,
   307  	}
   308  	c.Check(rsp.Status, check.Equals, 200)
   309  	c.Assert(rsp.Result, check.FitsTypeOf, expected)
   310  	c.Check(rsp.Result, check.DeepEquals, expected)
   311  
   312  	state.Lock()
   313  	user, err := auth.User(state, localUser.ID)
   314  	state.Unlock()
   315  	c.Check(err, check.IsNil)
   316  	c.Check(user.Username, check.Equals, "username")
   317  	c.Check(user.Email, check.Equals, expected.Email)
   318  	c.Check(user.Macaroon, check.Equals, localUser.Macaroon)
   319  	c.Check(user.Discharges, check.IsNil)
   320  	c.Check(user.StoreMacaroon, check.Equals, s.loginUserStoreMacaroon)
   321  	c.Check(user.StoreDischarges, check.DeepEquals, []string{"the-discharge-macaroon-serialized-data"})
   322  }
   323  
   324  func (s *userSuite) TestLogoutUser(c *check.C) {
   325  	state := s.d.Overlord().State()
   326  	state.Lock()
   327  	user, err := auth.NewUser(state, "username", "email@test.com", "macaroon", []string{"discharge"})
   328  	state.Unlock()
   329  	c.Assert(err, check.IsNil)
   330  
   331  	req, err := http.NewRequest("POST", "/v2/logout", nil)
   332  	c.Assert(err, check.IsNil)
   333  	req.Header.Set("Authorization", `Macaroon root="macaroon", discharge="discharge"`)
   334  
   335  	rsp := s.syncReq(c, req, user)
   336  	c.Check(rsp.Status, check.Equals, 200)
   337  
   338  	state.Lock()
   339  	_, err = auth.User(state, user.ID)
   340  	state.Unlock()
   341  	c.Check(err, check.Equals, auth.ErrInvalidUser)
   342  }
   343  
   344  func (s *userSuite) TestLoginUserBadRequest(c *check.C) {
   345  	buf := bytes.NewBufferString(`hello`)
   346  	req, err := http.NewRequest("POST", "/v2/login", buf)
   347  	c.Assert(err, check.IsNil)
   348  
   349  	rsp := s.errorReq(c, req, nil)
   350  	c.Check(rsp.Status, check.Equals, 400)
   351  	c.Check(rsp.Result, check.NotNil)
   352  }
   353  
   354  func (s *userSuite) TestLoginUserDeveloperAPIError(c *check.C) {
   355  	s.err = fmt.Errorf("error-from-login-user")
   356  	buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`)
   357  	req, err := http.NewRequest("POST", "/v2/login", buf)
   358  	c.Assert(err, check.IsNil)
   359  
   360  	rsp := s.errorReq(c, req, nil)
   361  	c.Check(rsp.Status, check.Equals, 401)
   362  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, testutil.Contains, "error-from-login-user")
   363  }
   364  
   365  func (s *userSuite) TestLoginUserTwoFactorRequiredError(c *check.C) {
   366  	s.err = store.ErrAuthenticationNeeds2fa
   367  	buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`)
   368  	req, err := http.NewRequest("POST", "/v2/login", buf)
   369  	c.Assert(err, check.IsNil)
   370  
   371  	rsp := s.errorReq(c, req, nil)
   372  	c.Check(rsp.Status, check.Equals, 401)
   373  	c.Check(rsp.Result.(*daemon.ErrorResult).Kind, check.Equals, client.ErrorKindTwoFactorRequired)
   374  }
   375  
   376  func (s *userSuite) TestLoginUserTwoFactorFailedError(c *check.C) {
   377  	s.err = store.Err2faFailed
   378  	buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`)
   379  	req, err := http.NewRequest("POST", "/v2/login", buf)
   380  	c.Assert(err, check.IsNil)
   381  
   382  	rsp := s.errorReq(c, req, nil)
   383  	c.Check(rsp.Status, check.Equals, 401)
   384  	c.Check(rsp.Result.(*daemon.ErrorResult).Kind, check.Equals, client.ErrorKindTwoFactorFailed)
   385  }
   386  
   387  func (s *userSuite) TestLoginUserInvalidCredentialsError(c *check.C) {
   388  	s.err = store.ErrInvalidCredentials
   389  	buf := bytes.NewBufferString(`{"username": "email@.com", "password": "password"}`)
   390  	req, err := http.NewRequest("POST", "/v2/login", buf)
   391  	c.Assert(err, check.IsNil)
   392  
   393  	rsp := s.errorReq(c, req, nil)
   394  	c.Check(rsp.Status, check.Equals, 401)
   395  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Equals, "invalid credentials")
   396  }
   397  
   398  func (s *userSuite) TestUsersOnlyRoot(c *check.C) {
   399  	for _, cmd := range daemon.APICommands() {
   400  		if strings.Contains(cmd.Path, "user") {
   401  			c.Check(cmd.RootOnly, check.Equals, true, check.Commentf(cmd.Path))
   402  		}
   403  	}
   404  }
   405  
   406  func (s *userSuite) TestPostCreateUserNoSSHKeys(c *check.C) {
   407  	s.userInfoExpectedEmail = "popper@lse.ac.uk"
   408  	s.userInfoResult = &store.User{
   409  		Username:         "karl",
   410  		OpenIDIdentifier: "xxyyzz",
   411  	}
   412  	buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail))
   413  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   414  	c.Assert(err, check.IsNil)
   415  
   416  	rsp := s.errorReq(c, req, nil)
   417  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user for "popper@lse.ac.uk": no ssh keys found`)
   418  }
   419  
   420  func (s *userSuite) TestPostCreateUser(c *check.C) {
   421  	s.testCreateUser(c, true)
   422  }
   423  
   424  func (s *userSuite) TestPostUserCreate(c *check.C) {
   425  	s.testCreateUser(c, false)
   426  }
   427  
   428  func (s *userSuite) testCreateUser(c *check.C, oldWay bool) {
   429  	expectedUsername := "karl"
   430  	s.userInfoExpectedEmail = "popper@lse.ac.uk"
   431  	s.userInfoResult = &store.User{
   432  		Username:         expectedUsername,
   433  		SSHKeys:          []string{"ssh1", "ssh2"},
   434  		OpenIDIdentifier: "xxyyzz",
   435  	}
   436  	defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error {
   437  		c.Check(username, check.Equals, expectedUsername)
   438  		c.Check(opts.SSHKeys, check.DeepEquals, []string{"ssh1", "ssh2"})
   439  		c.Check(opts.Gecos, check.Equals, "popper@lse.ac.uk,xxyyzz")
   440  		c.Check(opts.Sudoer, check.Equals, false)
   441  		return nil
   442  	})()
   443  
   444  	var req *http.Request
   445  	var expected interface{}
   446  	expectedItem := daemon.UserResponseData{
   447  		Username: expectedUsername,
   448  		SSHKeys:  []string{"ssh1", "ssh2"},
   449  	}
   450  
   451  	if oldWay {
   452  		var err error
   453  		buf := bytes.NewBufferString(fmt.Sprintf(`{"email": "%s"}`, s.userInfoExpectedEmail))
   454  		req, err = http.NewRequest("POST", "/v2/create-user", buf)
   455  		c.Assert(err, check.IsNil)
   456  		expected = &expectedItem
   457  	} else {
   458  		var err error
   459  		buf := bytes.NewBufferString(fmt.Sprintf(`{"action":"create","email": "%s"}`, s.userInfoExpectedEmail))
   460  		req, err = http.NewRequest("POST", "/v2/users", buf)
   461  		c.Assert(err, check.IsNil)
   462  		expected = []daemon.UserResponseData{expectedItem}
   463  	}
   464  
   465  	rsp := s.syncReq(c, req, nil)
   466  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   467  	c.Check(rsp.Result, check.DeepEquals, expected)
   468  
   469  	// user was setup in state
   470  	state := s.d.Overlord().State()
   471  	state.Lock()
   472  	user, err := auth.User(state, 1)
   473  	state.Unlock()
   474  	c.Check(err, check.IsNil)
   475  	c.Check(user.Username, check.Equals, expectedUsername)
   476  	c.Check(user.Email, check.Equals, s.userInfoExpectedEmail)
   477  	c.Check(user.Macaroon, check.NotNil)
   478  	// auth saved to user home dir
   479  	outfile := filepath.Join(s.mockUserHome, ".snap", "auth.json")
   480  	c.Check(osutil.FileExists(outfile), check.Equals, true)
   481  	c.Check(outfile, testutil.FileEquals,
   482  		fmt.Sprintf(`{"id":%d,"username":"%s","email":"%s","macaroon":"%s"}`,
   483  			1, expectedUsername, s.userInfoExpectedEmail, user.Macaroon))
   484  }
   485  
   486  func (s *userSuite) TestNoUserAdminCreateUser(c *check.C) { s.testNoUserAdmin(c, "/v2/create-user") }
   487  func (s *userSuite) TestNoUserAdminPostUser(c *check.C)   { s.testNoUserAdmin(c, "/v2/users") }
   488  func (s *userSuite) testNoUserAdmin(c *check.C, endpoint string) {
   489  	defer daemon.MockHasUserAdmin(false)()
   490  
   491  	buf := bytes.NewBufferString("{}")
   492  	req, err := http.NewRequest("POST", endpoint, buf)
   493  	c.Assert(err, check.IsNil)
   494  
   495  	rsp := s.errorReq(c, req, nil)
   496  
   497  	const noUserAdmin = "system user administration via snapd is not allowed on this system"
   498  	switch endpoint {
   499  	case "/v2/users":
   500  		c.Check(rsp, check.DeepEquals, daemon.MethodNotAllowed(noUserAdmin))
   501  	case "/v2/create-user":
   502  		c.Check(rsp, check.DeepEquals, daemon.Forbidden(noUserAdmin))
   503  	default:
   504  		c.Fatalf("unknown endpoint %q", endpoint)
   505  	}
   506  }
   507  
   508  func (s *userSuite) TestPostUserBadBody(c *check.C) {
   509  	buf := bytes.NewBufferString(`42`)
   510  	req, err := http.NewRequest("POST", "/v2/users", buf)
   511  	c.Assert(err, check.IsNil)
   512  
   513  	rsp := s.errorReq(c, req, nil)
   514  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, "cannot decode user action data from request body: .*")
   515  }
   516  
   517  func (s *userSuite) TestPostUserBadAfterBody(c *check.C) {
   518  	buf := bytes.NewBufferString(`{}42`)
   519  	req, err := http.NewRequest("POST", "/v2/users", buf)
   520  	c.Assert(err, check.IsNil)
   521  
   522  	rsp := s.errorReq(c, req, nil)
   523  	c.Check(rsp, check.DeepEquals, daemon.BadRequest("spurious content after user action"))
   524  }
   525  
   526  func (s *userSuite) TestPostUserNoAction(c *check.C) {
   527  	buf := bytes.NewBufferString("{}")
   528  	req, err := http.NewRequest("POST", "/v2/users", buf)
   529  	c.Assert(err, check.IsNil)
   530  
   531  	rsp := s.errorReq(c, req, nil)
   532  	c.Check(rsp, check.DeepEquals, daemon.BadRequest("missing user action"))
   533  }
   534  
   535  func (s *userSuite) TestPostUserBadAction(c *check.C) {
   536  	buf := bytes.NewBufferString(`{"action":"patatas"}`)
   537  	req, err := http.NewRequest("POST", "/v2/users", buf)
   538  	c.Assert(err, check.IsNil)
   539  
   540  	rsp := s.errorReq(c, req, nil)
   541  	c.Check(rsp, check.DeepEquals, daemon.BadRequest(`unsupported user action "patatas"`))
   542  }
   543  
   544  func (s *userSuite) TestPostUserActionRemoveNoUsername(c *check.C) {
   545  	buf := bytes.NewBufferString(`{"action":"remove"}`)
   546  	req, err := http.NewRequest("POST", "/v2/users", buf)
   547  	c.Assert(err, check.IsNil)
   548  
   549  	rsp := s.errorReq(c, req, nil)
   550  	c.Check(rsp, check.DeepEquals, daemon.BadRequest("need a username to remove"))
   551  }
   552  
   553  func (s *userSuite) TestPostUserActionRemoveDelUserErr(c *check.C) {
   554  	st := s.d.Overlord().State()
   555  	st.Lock()
   556  	_, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"})
   557  	st.Unlock()
   558  	c.Check(err, check.IsNil)
   559  
   560  	called := 0
   561  	defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error {
   562  		called++
   563  		c.Check(username, check.Equals, "some-user")
   564  		return fmt.Errorf("wat")
   565  	})()
   566  
   567  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   568  	req, err := http.NewRequest("POST", "/v2/users", buf)
   569  	c.Assert(err, check.IsNil)
   570  
   571  	rsp := s.errorReq(c, req, nil)
   572  	c.Check(rsp.Status, check.Equals, 500)
   573  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Equals, "wat")
   574  	c.Check(called, check.Equals, 1)
   575  }
   576  
   577  func (s *userSuite) TestPostUserActionRemoveStateErr(c *check.C) {
   578  	st := s.d.Overlord().State()
   579  	st.Lock()
   580  	st.Set("auth", 42) // breaks auth
   581  	st.Unlock()
   582  	called := 0
   583  	defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error {
   584  		called++
   585  		c.Check(username, check.Equals, "some-user")
   586  		return nil
   587  	})()
   588  
   589  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   590  	req, err := http.NewRequest("POST", "/v2/users", buf)
   591  	c.Assert(err, check.IsNil)
   592  
   593  	rsp := s.errorReq(c, req, nil)
   594  	c.Check(rsp.Status, check.Equals, 500)
   595  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `internal error: could not unmarshal state entry "auth": .*`)
   596  	c.Check(called, check.Equals, 0)
   597  }
   598  
   599  func (s *userSuite) TestPostUserActionRemoveNoUserInState(c *check.C) {
   600  	called := 0
   601  	defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error {
   602  		called++
   603  		c.Check(username, check.Equals, "some-user")
   604  		return nil
   605  	})
   606  
   607  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   608  	req, err := http.NewRequest("POST", "/v2/users", buf)
   609  	c.Assert(err, check.IsNil)
   610  
   611  	rsp := s.errorReq(c, req, nil)
   612  	c.Check(rsp, check.DeepEquals, daemon.BadRequest(`user "some-user" is not known`))
   613  	c.Check(called, check.Equals, 0)
   614  }
   615  
   616  func (s *userSuite) TestPostUserActionRemove(c *check.C) {
   617  	st := s.d.Overlord().State()
   618  	st.Lock()
   619  	user, err := auth.NewUser(st, "some-user", "email@test.com", "macaroon", []string{"discharge"})
   620  	st.Unlock()
   621  	c.Check(err, check.IsNil)
   622  
   623  	called := 0
   624  	defer daemon.MockOsutilDelUser(func(username string, opts *osutil.DelUserOptions) error {
   625  		called++
   626  		c.Check(username, check.Equals, "some-user")
   627  		return nil
   628  	})()
   629  
   630  	buf := bytes.NewBufferString(`{"action":"remove","username":"some-user"}`)
   631  	req, err := http.NewRequest("POST", "/v2/users", buf)
   632  	c.Assert(err, check.IsNil)
   633  	rsp := s.syncReq(c, req, nil)
   634  	c.Check(rsp.Status, check.Equals, 200)
   635  	expected := []daemon.UserResponseData{
   636  		{ID: user.ID, Username: user.Username, Email: user.Email},
   637  	}
   638  	c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{
   639  		"removed": expected,
   640  	})
   641  	c.Check(called, check.Equals, 1)
   642  
   643  	// and the user is removed from state
   644  	st.Lock()
   645  	_, err = auth.User(st, user.ID)
   646  	st.Unlock()
   647  	c.Check(err, check.Equals, auth.ErrInvalidUser)
   648  }
   649  
   650  func (s *userSuite) setupSigner(accountID string, signerPrivKey asserts.PrivateKey) *assertstest.SigningDB {
   651  	st := s.d.Overlord().State()
   652  
   653  	signerSigning := s.Brands.Register(accountID, signerPrivKey, map[string]interface{}{
   654  		"account-id":   accountID,
   655  		"verification": "verified",
   656  	})
   657  	acctNKey := s.Brands.AccountsAndKeys(accountID)
   658  
   659  	assertstest.AddMany(s.StoreSigning, acctNKey...)
   660  	assertstatetest.AddMany(st, acctNKey...)
   661  
   662  	return signerSigning
   663  }
   664  
   665  var (
   666  	partnerPrivKey, _ = assertstest.GenerateKey(752)
   667  	unknownPrivKey, _ = assertstest.GenerateKey(752)
   668  )
   669  
   670  func (s *userSuite) makeSystemUsers(c *check.C, systemUsers []map[string]interface{}) {
   671  	st := s.d.Overlord().State()
   672  	st.Lock()
   673  	defer st.Unlock()
   674  
   675  	assertstatetest.AddMany(st, s.StoreSigning.StoreAccountKey(""))
   676  
   677  	s.setupSigner("my-brand", brandPrivKey)
   678  	s.setupSigner("partner", partnerPrivKey)
   679  	s.setupSigner("unknown", unknownPrivKey)
   680  
   681  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
   682  		"architecture":          "amd64",
   683  		"gadget":                "pc",
   684  		"kernel":                "pc-kernel",
   685  		"required-snaps":        []interface{}{"required-snap1"},
   686  		"system-user-authority": []interface{}{"my-brand", "partner"},
   687  	})
   688  	// now add model related stuff to the system
   689  	assertstatetest.AddMany(st, model)
   690  	// and a serial
   691  	deviceKey, _ := assertstest.GenerateKey(752)
   692  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
   693  	c.Assert(err, check.IsNil)
   694  	serial, err := s.Brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
   695  		"authority-id":        "my-brand",
   696  		"brand-id":            "my-brand",
   697  		"model":               "my-model",
   698  		"serial":              "serialserial",
   699  		"device-key":          string(encDevKey),
   700  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
   701  		"timestamp":           time.Now().Format(time.RFC3339),
   702  	}, nil, "")
   703  	c.Assert(err, check.IsNil)
   704  	assertstatetest.AddMany(st, serial)
   705  
   706  	for _, suMap := range systemUsers {
   707  		su, err := s.Brands.Signing(suMap["authority-id"].(string)).Sign(asserts.SystemUserType, suMap, nil, "")
   708  		c.Assert(err, check.IsNil)
   709  		su = su.(*asserts.SystemUser)
   710  		// now add system-user assertion to the system
   711  		assertstatetest.AddMany(st, su)
   712  	}
   713  	// create fake device
   714  	err = devicestatetest.SetDevice(st, &auth.DeviceState{
   715  		Brand:  "my-brand",
   716  		Model:  "my-model",
   717  		Serial: "serialserial",
   718  	})
   719  	c.Assert(err, check.IsNil)
   720  }
   721  
   722  var goodUser = map[string]interface{}{
   723  	"authority-id": "my-brand",
   724  	"brand-id":     "my-brand",
   725  	"email":        "foo@bar.com",
   726  	"series":       []interface{}{"16", "18"},
   727  	"models":       []interface{}{"my-model", "other-model"},
   728  	"name":         "Boring Guy",
   729  	"username":     "guy",
   730  	"password":     "$6$salt$hash",
   731  	"since":        time.Now().Format(time.RFC3339),
   732  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   733  }
   734  
   735  var partnerUser = map[string]interface{}{
   736  	"authority-id": "partner",
   737  	"brand-id":     "my-brand",
   738  	"email":        "p@partner.com",
   739  	"series":       []interface{}{"16", "18"},
   740  	"models":       []interface{}{"my-model"},
   741  	"name":         "Partner Guy",
   742  	"username":     "partnerguy",
   743  	"password":     "$6$salt$hash",
   744  	"since":        time.Now().Format(time.RFC3339),
   745  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   746  }
   747  
   748  var serialUser = map[string]interface{}{
   749  	"format":       "1",
   750  	"authority-id": "my-brand",
   751  	"brand-id":     "my-brand",
   752  	"email":        "serial@bar.com",
   753  	"series":       []interface{}{"16", "18"},
   754  	"models":       []interface{}{"my-model"},
   755  	"serials":      []interface{}{"serialserial"},
   756  	"name":         "Serial Guy",
   757  	"username":     "goodserialguy",
   758  	"password":     "$6$salt$hash",
   759  	"since":        time.Now().Format(time.RFC3339),
   760  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   761  }
   762  
   763  var badUser = map[string]interface{}{
   764  	// bad user (not valid for this model)
   765  	"authority-id": "my-brand",
   766  	"brand-id":     "my-brand",
   767  	"email":        "foobar@bar.com",
   768  	"series":       []interface{}{"16", "18"},
   769  	"models":       []interface{}{"non-of-the-models-i-have"},
   770  	"name":         "Random Gal",
   771  	"username":     "gal",
   772  	"password":     "$6$salt$hash",
   773  	"since":        time.Now().Format(time.RFC3339),
   774  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   775  }
   776  
   777  var badUserNoMatchingSerial = map[string]interface{}{
   778  	"format":       "1",
   779  	"authority-id": "my-brand",
   780  	"brand-id":     "my-brand",
   781  	"email":        "noserial@bar.com",
   782  	"series":       []interface{}{"16", "18"},
   783  	"models":       []interface{}{"my-model"},
   784  	"serials":      []interface{}{"different-serialserial"},
   785  	"name":         "No Serial Guy",
   786  	"username":     "noserial",
   787  	"password":     "$6$salt$hash",
   788  	"since":        time.Now().Format(time.RFC3339),
   789  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   790  }
   791  
   792  var unknownUser = map[string]interface{}{
   793  	"authority-id": "unknown",
   794  	"brand-id":     "my-brand",
   795  	"email":        "x@partner.com",
   796  	"series":       []interface{}{"16", "18"},
   797  	"models":       []interface{}{"my-model"},
   798  	"name":         "XGuy",
   799  	"username":     "xguy",
   800  	"password":     "$6$salt$hash",
   801  	"since":        time.Now().Format(time.RFC3339),
   802  	"until":        time.Now().Add(24 * 30 * time.Hour).Format(time.RFC3339),
   803  }
   804  
   805  func (s *userSuite) TestGetUserDetailsFromAssertionHappy(c *check.C) {
   806  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   807  
   808  	st := s.d.Overlord().State()
   809  
   810  	st.Lock()
   811  	model, err := s.d.Overlord().DeviceManager().Model()
   812  	st.Unlock()
   813  	c.Assert(err, check.IsNil)
   814  
   815  	// ensure that if we query the details from the assert DB we get
   816  	// the expected user
   817  	username, opts, err := daemon.GetUserDetailsFromAssertion(st, model, nil, "foo@bar.com")
   818  	c.Check(username, check.Equals, "guy")
   819  	c.Check(opts, check.DeepEquals, &osutil.AddUserOptions{
   820  		Gecos:    "foo@bar.com,Boring Guy",
   821  		Password: "$6$salt$hash",
   822  	})
   823  	c.Check(err, check.IsNil)
   824  }
   825  
   826  // FIXME: These tests all look similar, with small deltas. Would be
   827  // nice to transform them into a table that is just the deltas, and
   828  // run on a loop.
   829  func (s *userSuite) TestPostCreateUserFromAssertion(c *check.C) {
   830  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   831  
   832  	// mock the calls that create the user
   833  	defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error {
   834  		c.Check(username, check.Equals, "guy")
   835  		c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
   836  		c.Check(opts.Sudoer, check.Equals, false)
   837  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
   838  		c.Check(opts.ForcePasswordChange, check.Equals, false)
   839  		return nil
   840  	})()
   841  
   842  	// do it!
   843  	buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`)
   844  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   845  	c.Assert(err, check.IsNil)
   846  
   847  	rsp := s.syncReq(c, req, nil)
   848  
   849  	expected := &daemon.UserResponseData{
   850  		Username: "guy",
   851  	}
   852  
   853  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   854  	c.Check(rsp.Result, check.DeepEquals, expected)
   855  
   856  	// ensure the user was added to the state
   857  	st := s.d.Overlord().State()
   858  	st.Lock()
   859  	users, err := auth.Users(st)
   860  	c.Assert(err, check.IsNil)
   861  	st.Unlock()
   862  	c.Check(users, check.HasLen, 1)
   863  }
   864  
   865  func (s *userSuite) TestPostCreateUserFromAssertionWithForcePasswordChange(c *check.C) {
   866  	user := make(map[string]interface{})
   867  	for k, v := range goodUser {
   868  		user[k] = v
   869  	}
   870  	user["force-password-change"] = "true"
   871  	lusers := []map[string]interface{}{user}
   872  	s.makeSystemUsers(c, lusers)
   873  
   874  	// mock the calls that create the user
   875  	defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error {
   876  		c.Check(username, check.Equals, "guy")
   877  		c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
   878  		c.Check(opts.Sudoer, check.Equals, false)
   879  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
   880  		c.Check(opts.ForcePasswordChange, check.Equals, true)
   881  		return nil
   882  	})()
   883  
   884  	// do it!
   885  	buf := bytes.NewBufferString(`{"email": "foo@bar.com","known":true}`)
   886  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   887  	c.Assert(err, check.IsNil)
   888  
   889  	rsp := s.syncReq(c, req, nil)
   890  
   891  	expected := &daemon.UserResponseData{
   892  		Username: "guy",
   893  	}
   894  
   895  	c.Check(rsp.Result, check.FitsTypeOf, expected)
   896  	c.Check(rsp.Result, check.DeepEquals, expected)
   897  
   898  	// ensure the user was added to the state
   899  	st := s.d.Overlord().State()
   900  	st.Lock()
   901  	users, err := auth.Users(st)
   902  	c.Assert(err, check.IsNil)
   903  	st.Unlock()
   904  	c.Check(users, check.HasLen, 1)
   905  }
   906  
   907  func (s *userSuite) TestPostCreateUserFromAssertionAllKnown(c *check.C) {
   908  	expectSudoer := false
   909  	s.testPostCreateUserFromAssertion(c, `{"known":true}`, expectSudoer)
   910  }
   911  
   912  func (s *userSuite) TestPostCreateUserFromAssertionAllAutomatic(c *check.C) {
   913  	// automatic implies "sudoder"
   914  	expectSudoer := true
   915  	s.testPostCreateUserFromAssertion(c, `{"automatic":true}`, expectSudoer)
   916  }
   917  
   918  func (s *userSuite) testPostCreateUserFromAssertion(c *check.C, postData string, expectSudoer bool) {
   919  	s.makeSystemUsers(c, []map[string]interface{}{goodUser, partnerUser, serialUser, badUser, badUserNoMatchingSerial, unknownUser})
   920  	created := map[string]bool{}
   921  	// mock the calls that create the user
   922  	defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error {
   923  		switch username {
   924  		case "guy":
   925  			c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
   926  		case "partnerguy":
   927  			c.Check(opts.Gecos, check.Equals, "p@partner.com,Partner Guy")
   928  		case "goodserialguy":
   929  			c.Check(opts.Gecos, check.Equals, "serial@bar.com,Serial Guy")
   930  		default:
   931  			c.Logf("unexpected username %q", username)
   932  			c.Fail()
   933  		}
   934  		c.Check(opts.Sudoer, check.Equals, expectSudoer)
   935  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
   936  		created[username] = true
   937  		return nil
   938  	})()
   939  	// make sure we report them as non-existing until created
   940  	defer daemon.MockUserLookup(func(username string) (*user.User, error) {
   941  		if created[username] {
   942  			return s.trivialUserLookup(username)
   943  		}
   944  		return nil, fmt.Errorf("not created yet")
   945  	})()
   946  
   947  	// do it!
   948  	buf := bytes.NewBufferString(postData)
   949  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   950  	c.Assert(err, check.IsNil)
   951  
   952  	rsp := s.syncReq(c, req, nil)
   953  
   954  	// note that we get a list here instead of a single
   955  	// userResponseData item
   956  	c.Check(rsp.Result, check.FitsTypeOf, []daemon.UserResponseData{})
   957  	seen := map[string]bool{}
   958  	for _, u := range rsp.Result.([]daemon.UserResponseData) {
   959  		seen[u.Username] = true
   960  		c.Check(u, check.DeepEquals, daemon.UserResponseData{Username: u.Username})
   961  	}
   962  	c.Check(seen, check.DeepEquals, map[string]bool{
   963  		"guy":           true,
   964  		"partnerguy":    true,
   965  		"goodserialguy": true,
   966  	})
   967  
   968  	// ensure the user was added to the state
   969  	st := s.d.Overlord().State()
   970  	st.Lock()
   971  	users, err := auth.Users(st)
   972  	c.Assert(err, check.IsNil)
   973  	st.Unlock()
   974  	c.Check(users, check.HasLen, 3)
   975  }
   976  
   977  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownClassicErrors(c *check.C) {
   978  	restore := release.MockOnClassic(true)
   979  	defer restore()
   980  
   981  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   982  
   983  	// do it!
   984  	buf := bytes.NewBufferString(`{"known":true}`)
   985  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
   986  	c.Assert(err, check.IsNil)
   987  
   988  	rsp := s.errorReq(c, req, nil)
   989  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user: device is a classic system`)
   990  }
   991  
   992  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwnedErrors(c *check.C) {
   993  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
   994  
   995  	st := s.d.Overlord().State()
   996  	st.Lock()
   997  	_, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"})
   998  	st.Unlock()
   999  	c.Check(err, check.IsNil)
  1000  
  1001  	// do it!
  1002  	buf := bytes.NewBufferString(`{"known":true}`)
  1003  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
  1004  	c.Assert(err, check.IsNil)
  1005  
  1006  	rsp := s.errorReq(c, req, nil)
  1007  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user: device already managed`)
  1008  }
  1009  
  1010  func (s *userSuite) TestPostCreateUserAutomaticManagedDoesNotActOrError(c *check.C) {
  1011  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
  1012  
  1013  	st := s.d.Overlord().State()
  1014  	st.Lock()
  1015  	_, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"})
  1016  	st.Unlock()
  1017  	c.Check(err, check.IsNil)
  1018  
  1019  	// do it!
  1020  	buf := bytes.NewBufferString(`{"automatic":true}`)
  1021  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
  1022  	c.Assert(err, check.IsNil)
  1023  
  1024  	rsp := s.syncReq(c, req, nil)
  1025  
  1026  	// expecting an empty reply
  1027  	expected := []daemon.UserResponseData{}
  1028  	c.Check(rsp.Result, check.FitsTypeOf, expected)
  1029  	c.Check(rsp.Result, check.DeepEquals, expected)
  1030  }
  1031  
  1032  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownNoModelError(c *check.C) {
  1033  	restore := release.MockOnClassic(false)
  1034  	defer restore()
  1035  
  1036  	st := s.d.Overlord().State()
  1037  	// have not model yet
  1038  	st.Lock()
  1039  	err := devicestatetest.SetDevice(st, &auth.DeviceState{})
  1040  	st.Unlock()
  1041  	c.Assert(err, check.IsNil)
  1042  
  1043  	// do it!
  1044  	buf := bytes.NewBufferString(`{"known":true}`)
  1045  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
  1046  	c.Assert(err, check.IsNil)
  1047  
  1048  	rsp := s.errorReq(c, req, nil)
  1049  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, `cannot create user: cannot get model assertion: no state entry for key`)
  1050  }
  1051  
  1052  func (s *userSuite) TestPostCreateUserFromAssertionNoModel(c *check.C) {
  1053  	restore := release.MockOnClassic(false)
  1054  	defer restore()
  1055  
  1056  	s.makeSystemUsers(c, []map[string]interface{}{serialUser})
  1057  	model := s.Brands.Model("my-brand", "other-model", map[string]interface{}{
  1058  		"architecture":          "amd64",
  1059  		"gadget":                "pc",
  1060  		"kernel":                "pc-kernel",
  1061  		"system-user-authority": []interface{}{"my-brand", "partner"},
  1062  	})
  1063  
  1064  	st := s.d.Overlord().State()
  1065  	st.Lock()
  1066  	assertstatetest.AddMany(st, model)
  1067  	err := devicestatetest.SetDevice(st, &auth.DeviceState{
  1068  		Brand:  "my-brand",
  1069  		Model:  "my-model",
  1070  		Serial: "other-serial-assertion",
  1071  	})
  1072  	st.Unlock()
  1073  	c.Assert(err, check.IsNil)
  1074  
  1075  	// do it!
  1076  	buf := bytes.NewBufferString(`{"email":"serial@bar.com", "known":true}`)
  1077  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
  1078  	c.Assert(err, check.IsNil)
  1079  
  1080  	rsp := s.errorReq(c, req, nil)
  1081  	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`)
  1082  }
  1083  
  1084  func (s *userSuite) TestPostCreateUserFromAssertionAllKnownButOwned(c *check.C) {
  1085  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
  1086  
  1087  	st := s.d.Overlord().State()
  1088  	st.Lock()
  1089  	_, err := auth.NewUser(st, "username", "email@test.com", "macaroon", []string{"discharge"})
  1090  	st.Unlock()
  1091  	c.Check(err, check.IsNil)
  1092  
  1093  	// mock the calls that create the user
  1094  	created := map[string]bool{}
  1095  	defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error {
  1096  		c.Check(username, check.Equals, "guy")
  1097  		c.Check(opts.Gecos, check.Equals, "foo@bar.com,Boring Guy")
  1098  		c.Check(opts.Sudoer, check.Equals, false)
  1099  		c.Check(opts.Password, check.Equals, "$6$salt$hash")
  1100  		created[username] = true
  1101  		return nil
  1102  	})()
  1103  	// make sure we report them as non-existing until created
  1104  	defer daemon.MockUserLookup(func(username string) (*user.User, error) {
  1105  		if created[username] {
  1106  			return s.trivialUserLookup(username)
  1107  		}
  1108  		return nil, fmt.Errorf("not created yet")
  1109  	})()
  1110  
  1111  	// do it!
  1112  	buf := bytes.NewBufferString(`{"known":true,"force-managed":true}`)
  1113  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
  1114  	c.Assert(err, check.IsNil)
  1115  
  1116  	rsp := s.syncReq(c, req, nil)
  1117  
  1118  	// note that we get a list here instead of a single
  1119  	// userResponseData item
  1120  	expected := []daemon.UserResponseData{
  1121  		{Username: "guy"},
  1122  	}
  1123  	c.Check(rsp.Result, check.FitsTypeOf, expected)
  1124  	c.Check(rsp.Result, check.DeepEquals, expected)
  1125  }
  1126  
  1127  func (s *userSuite) TestPostCreateUserAutomaticDisabled(c *check.C) {
  1128  	s.makeSystemUsers(c, []map[string]interface{}{goodUser})
  1129  
  1130  	// disable automatic user creation
  1131  	st := s.d.Overlord().State()
  1132  	st.Lock()
  1133  	tr := config.NewTransaction(st)
  1134  	err := tr.Set("core", "users.create.automatic", false)
  1135  	tr.Commit()
  1136  	st.Unlock()
  1137  	c.Assert(err, check.IsNil)
  1138  
  1139  	defer daemon.MockOsutilAddUser(func(username string, opts *osutil.AddUserOptions) error {
  1140  		// we should not reach here
  1141  		panic("no user should be created")
  1142  	})()
  1143  	// make sure we report them as non-existing until created
  1144  	defer daemon.MockUserLookup(func(username string) (*user.User, error) {
  1145  		// this error would simply be interpreted as need to create
  1146  		return nil, fmt.Errorf("not created yet")
  1147  	})()
  1148  
  1149  	// do it!
  1150  	buf := bytes.NewBufferString(`{"automatic": true}`)
  1151  	req, err := http.NewRequest("POST", "/v2/create-user", buf)
  1152  	c.Assert(err, check.IsNil)
  1153  
  1154  	rsp := s.syncReq(c, req, nil)
  1155  
  1156  	// empty result
  1157  	expected := []daemon.UserResponseData{}
  1158  	c.Check(rsp.Result, check.FitsTypeOf, expected)
  1159  	c.Check(rsp.Result, check.DeepEquals, expected)
  1160  
  1161  	// ensure no user was added to the state
  1162  	st.Lock()
  1163  	users, err := auth.Users(st)
  1164  	c.Assert(err, check.IsNil)
  1165  	st.Unlock()
  1166  	c.Check(users, check.HasLen, 0)
  1167  }
  1168  
  1169  func (s *userSuite) TestUsersEmpty(c *check.C) {
  1170  	req, err := http.NewRequest("GET", "/v2/users", nil)
  1171  	c.Assert(err, check.IsNil)
  1172  
  1173  	rsp := s.syncReq(c, req, nil)
  1174  
  1175  	expected := []daemon.UserResponseData{}
  1176  	c.Check(rsp.Result, check.FitsTypeOf, expected)
  1177  	c.Check(rsp.Result, check.DeepEquals, expected)
  1178  }
  1179  
  1180  func (s *userSuite) TestUsersHasUser(c *check.C) {
  1181  	st := s.d.Overlord().State()
  1182  	st.Lock()
  1183  	u, err := auth.NewUser(st, "someuser", "mymail@test.com", "macaroon", []string{"discharge"})
  1184  	st.Unlock()
  1185  	c.Assert(err, check.IsNil)
  1186  
  1187  	req, err := http.NewRequest("GET", "/v2/users", nil)
  1188  	c.Assert(err, check.IsNil)
  1189  
  1190  	rsp := s.syncReq(c, req, nil)
  1191  
  1192  	expected := []daemon.UserResponseData{
  1193  		{ID: u.ID, Username: u.Username, Email: u.Email},
  1194  	}
  1195  	c.Check(rsp.Result, check.FitsTypeOf, expected)
  1196  	c.Check(rsp.Result, check.DeepEquals, expected)
  1197  }