func (as *authSuite) TestNewUserAddsToExistent(c *C) { as.state.Lock() firstUser, err := auth.NewUser(as.state, "username", "*****@*****.**", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) // adding a new one as.state.Lock() user, err := auth.NewUser(as.state, "new_username", "*****@*****.**", "new_macaroon", []string{"new_discharge"}) as.state.Unlock() c.Check(err, IsNil) c.Check(user.ID, Equals, 2) c.Check(user.Username, Equals, "new_username") c.Check(user.Email, Equals, "*****@*****.**") as.state.Lock() userFromState, err := auth.User(as.state, 2) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState.ID, Equals, 2) c.Check(userFromState.Username, Equals, "new_username") c.Check(userFromState.Email, Equals, "*****@*****.**") // first user is still in the state as.state.Lock() userFromState, err = auth.User(as.state, 1) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, firstUser) }
func (as *authSuite) TestNewUserAddsToExistent(c *C) { as.state.Lock() firstUser, err := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) // adding a new one as.state.Lock() user, err := auth.NewUser(as.state, "new_username", "new_macaroon", []string{"new_discharge"}) as.state.Unlock() expected := &auth.UserState{ ID: 2, Username: "******", Macaroon: "new_macaroon", Discharges: []string{"new_discharge"}, StoreMacaroon: "new_macaroon", StoreDischarges: []string{"new_discharge"}, } c.Check(err, IsNil) c.Check(user, DeepEquals, expected) as.state.Lock() userFromState, err := auth.User(as.state, 2) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, expected) // first user is still in the state as.state.Lock() userFromState, err = auth.User(as.state, 1) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, firstUser) }
func (as *authSuite) TestUsers(c *C) { as.state.Lock() user1, err1 := auth.NewUser(as.state, "user1", "*****@*****.**", "macaroon", []string{"discharge"}) user2, err2 := auth.NewUser(as.state, "user2", "*****@*****.**", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err1, IsNil) c.Check(err2, IsNil) as.state.Lock() users, err := auth.Users(as.state) as.state.Unlock() c.Check(err, IsNil) c.Check(users, DeepEquals, []*auth.UserState{user1, user2}) }
func (as *authSuite) TestRemove(c *C) { as.state.Lock() user, err := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) as.state.Lock() _, err = auth.User(as.state, user.ID) as.state.Unlock() c.Check(err, IsNil) as.state.Lock() err = auth.RemoveUser(as.state, user.ID) as.state.Unlock() c.Assert(err, IsNil) as.state.Lock() _, err = auth.User(as.state, user.ID) as.state.Unlock() c.Check(err, ErrorMatches, "invalid user") as.state.Lock() err = auth.RemoveUser(as.state, user.ID) as.state.Unlock() c.Assert(err, ErrorMatches, "invalid user") }
func (as *authSuite) TestAuthContextUpdateUserAuthOtherUpdate(c *C) { as.state.Lock() user, _ := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) otherUpdateUser := *user otherUpdateUser.Macaroon = "macaroon2" otherUpdateUser.StoreDischarges = []string{"other-discharges"} err := auth.UpdateUser(as.state, &otherUpdateUser) as.state.Unlock() c.Assert(err, IsNil) newDischarges := []string{"updated-discharge"} authContext := auth.NewAuthContext(as.state, nil) // last discharges win curUser, err := authContext.UpdateUserAuth(user, newDischarges) c.Assert(err, IsNil) as.state.Lock() userFromState, err := auth.User(as.state, user.ID) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, curUser) c.Check(curUser, DeepEquals, &auth.UserState{ ID: user.ID, Username: "******", Macaroon: "macaroon2", Discharges: []string{"discharge"}, StoreMacaroon: "macaroon", StoreDischarges: newDischarges, }) }
func (as *authSuite) TestNewUser(c *C) { as.state.Lock() user, err := auth.NewUser(as.state, "username", "*****@*****.**", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) // check snapd macaroon was generated for the local user var authStateData auth.AuthState as.state.Lock() err = as.state.Get("auth", &authStateData) as.state.Unlock() c.Check(err, IsNil) c.Check(authStateData.MacaroonKey, NotNil) expectedMacaroon, err := macaroon.New(authStateData.MacaroonKey, "1", "snapd") c.Check(err, IsNil) expectedSerializedMacaroon, err := auth.MacaroonSerialize(expectedMacaroon) c.Check(err, IsNil) expected := &auth.UserState{ ID: 1, Username: "******", Email: "*****@*****.**", Macaroon: expectedSerializedMacaroon, Discharges: nil, StoreMacaroon: "macaroon", StoreDischarges: []string{"discharge"}, } c.Check(user, DeepEquals, expected) }
func (as *authSuite) TestUserForNonExistent(c *C) { as.state.Lock() _, err := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) as.state.Lock() userFromState, err := auth.User(as.state, 42) c.Check(err, ErrorMatches, "invalid user") c.Check(userFromState, IsNil) }
func (as *authSuite) TestUser(c *C) { as.state.Lock() user, err := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) as.state.Lock() userFromState, err := auth.User(as.state, 1) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, user) }
func (as *authSuite) TestCheckMacaroonValidUser(c *C) { as.state.Lock() expectedUser, err := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) as.state.Lock() user, err := auth.CheckMacaroon(as.state, "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) c.Check(user, DeepEquals, expectedUser) }
func (as *authSuite) TestNewUserSortsDischarges(c *C) { as.state.Lock() user, err := auth.NewUser(as.state, "", "*****@*****.**", "macaroon", []string{"discharge2", "discharge1"}) as.state.Unlock() expected := []string{"discharge1", "discharge2"} c.Check(user.StoreDischarges, DeepEquals, expected) as.state.Lock() userFromState, err := auth.User(as.state, 1) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState.StoreDischarges, DeepEquals, expected) }
func (as *authSuite) TestAuthContextUpdateUserAuthInvalid(c *C) { as.state.Lock() _, _ = auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() user := &auth.UserState{ ID: 102, Username: "******", Macaroon: "macaroon", } authContext := auth.NewAuthContext(as.state, nil) _, err := authContext.UpdateUserAuth(user, nil) c.Assert(err, ErrorMatches, "invalid user") }
func (as *authSuite) TestUpdateUserInvalid(c *C) { as.state.Lock() _, _ = auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() user := &auth.UserState{ ID: 102, Username: "******", Macaroon: "macaroon", } as.state.Lock() err := auth.UpdateUser(as.state, user) as.state.Unlock() c.Assert(err, ErrorMatches, "invalid user") }
func (as *authSuite) TestAuthContextUpdateUserAuth(c *C) { as.state.Lock() user, _ := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() newDischarges := []string{"updated-discharge"} authContext := auth.NewAuthContext(as.state, nil) user, err := authContext.UpdateUserAuth(user, newDischarges) c.Check(err, IsNil) as.state.Lock() userFromState, err := auth.User(as.state, user.ID) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, user) c.Check(userFromState.Discharges, DeepEquals, []string{"discharge"}) c.Check(user.StoreDischarges, DeepEquals, newDischarges) }
func (as *authSuite) TestUpdateUser(c *C) { as.state.Lock() user, _ := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() user.Username = "******" user.StoreDischarges = []string{"updated-discharge"} as.state.Lock() err := auth.UpdateUser(as.state, user) as.state.Unlock() c.Check(err, IsNil) as.state.Lock() userFromState, err := auth.User(as.state, user.ID) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, user) }
func (as *authSuite) TestCheckMacaroonInvalidAuth(c *C) { as.state.Lock() user, err := auth.CheckMacaroon(as.state, "other-macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, Equals, auth.ErrInvalidAuth) c.Check(user, IsNil) as.state.Lock() _, err = auth.NewUser(as.state, "username", "macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, IsNil) as.state.Lock() user, err = auth.CheckMacaroon(as.state, "other-macaroon", []string{"discharge"}) as.state.Unlock() c.Check(err, Equals, auth.ErrInvalidAuth) c.Check(user, IsNil) }
func (as *authSuite) TestNewUserSortsDischarges(c *C) { as.state.Lock() user, err := auth.NewUser(as.state, "username", "macaroon", []string{"discharge2", "discharge1"}) as.state.Unlock() expected := &auth.UserState{ ID: 1, Username: "******", Macaroon: "macaroon", Discharges: []string{"discharge1", "discharge2"}, StoreMacaroon: "macaroon", StoreDischarges: []string{"discharge1", "discharge2"}, } c.Check(err, IsNil) c.Check(user, DeepEquals, expected) as.state.Lock() userFromState, err := auth.User(as.state, 1) as.state.Unlock() c.Check(err, IsNil) c.Check(userFromState, DeepEquals, expected) }
func (as *authSuite) TestCheckMacaroonValidUserOldStyle(c *C) { // create a fake store-deserializable macaroon m, err := macaroon.New([]byte("secret"), "some-id", "location") c.Check(err, IsNil) serializedMacaroon, err := auth.MacaroonSerialize(m) c.Check(err, IsNil) as.state.Lock() expectedUser, err := auth.NewUser(as.state, "username", "*****@*****.**", serializedMacaroon, []string{"discharge"}) c.Check(err, IsNil) // set user local macaroons with store macaroons expectedUser.Macaroon = expectedUser.StoreMacaroon expectedUser.Discharges = expectedUser.StoreDischarges err = auth.UpdateUser(as.state, expectedUser) c.Check(err, IsNil) as.state.Unlock() as.state.Lock() user, err := auth.CheckMacaroon(as.state, expectedUser.Macaroon, expectedUser.Discharges) as.state.Unlock() c.Check(err, IsNil) c.Check(user, DeepEquals, expectedUser) }
func (as *authSuite) TestCheckMacaroonInvalidAuthMalformedMacaroon(c *C) { var authStateData auth.AuthState as.state.Lock() // create a new user to ensure there is a MacaroonKey setup _, err := auth.NewUser(as.state, "username", "*****@*****.**", "macaroon", []string{"discharge"}) c.Check(err, IsNil) // get AuthState to get signing MacaroonKey err = as.state.Get("auth", &authStateData) c.Check(err, IsNil) as.state.Unlock() // setup a macaroon for an invalid user invalidMacaroon, err := macaroon.New(authStateData.MacaroonKey, "invalid", "snapd") c.Check(err, IsNil) serializedInvalidMacaroon, err := auth.MacaroonSerialize(invalidMacaroon) c.Check(err, IsNil) as.state.Lock() user, err := auth.CheckMacaroon(as.state, serializedInvalidMacaroon, nil) as.state.Unlock() c.Check(err, Equals, auth.ErrInvalidAuth) c.Check(user, IsNil) }
func loginUser(c *Command, r *http.Request, user *auth.UserState) Response { var loginData struct { Username string `json:"username"` Password string `json:"password"` Otp string `json:"otp"` } decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&loginData); err != nil { return BadRequest("cannot decode login data from request body: %v", err) } // the "username" needs to look a lot like an email address if !isEmailish(loginData.Username) { return SyncResponse(&resp{ Type: ResponseTypeError, Result: &errorResult{ Message: "please use a valid email address.", Kind: errorKindInvalidAuthData, Value: map[string][]string{"email": {"invalid"}}, }, Status: http.StatusBadRequest, }, nil) } macaroon, discharge, err := store.LoginUser(loginData.Username, loginData.Password, loginData.Otp) switch err { case store.ErrAuthenticationNeeds2fa: return SyncResponse(&resp{ Type: ResponseTypeError, Result: &errorResult{ Kind: errorKindTwoFactorRequired, Message: err.Error(), }, Status: http.StatusUnauthorized, }, nil) case store.Err2faFailed: return SyncResponse(&resp{ Type: ResponseTypeError, Result: &errorResult{ Kind: errorKindTwoFactorFailed, Message: err.Error(), }, Status: http.StatusUnauthorized, }, nil) default: if err, ok := err.(store.ErrInvalidAuthData); ok { return SyncResponse(&resp{ Type: ResponseTypeError, Result: &errorResult{ Message: err.Error(), Kind: errorKindInvalidAuthData, Value: err, }, Status: http.StatusBadRequest, }, nil) } return Unauthorized(err.Error()) case nil: // continue } overlord := c.d.overlord state := overlord.State() state.Lock() _, err = auth.NewUser(state, loginData.Username, macaroon, []string{discharge}) state.Unlock() if err != nil { return InternalError("cannot persist authentication details: %v", err) } result := loginResponseData{ Macaroon: macaroon, Discharges: []string{discharge}, } return SyncResponse(result, nil) }