github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/daemon/api_users_test.go (about)

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