github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/daemon/api_users_test.go (about)

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