summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <mvo@ubuntu.com>2016-09-14 08:56:44 +0200
committerMichael Vogt <mvo@ubuntu.com>2016-09-14 08:56:44 +0200
commitd437bf4ff15ac815fa8777e60e037b3a0df0d0a3 (patch)
tree0a1589a4e6c6594182f416838df07826b7ac13df
parent6dde596a31a8d53f55e76b55ea610f585faf5c2f (diff)
parentdd64c4015e73475fdca468944bbd06aa8fa587a1 (diff)
Merge remote-tracking branch 'upstream/master' into bugfix/autopkgtest-homebugfix/autopkgtest-home
-rw-r--r--.gitignore2
-rw-r--r--asserts/gpgkeypairmgr.go12
-rw-r--r--cmd/snap/cmd_sign_test.go1
-rwxr-xr-xdebian/rules2
-rw-r--r--overlord/auth/auth.go44
-rw-r--r--overlord/auth/auth_test.go80
-rwxr-xr-xrun-checks8
-rw-r--r--store/store.go58
-rw-r--r--store/store_test.go1237
-rw-r--r--tests/lib/fakestore/store/store_test.go1
-rw-r--r--tests/main/create-key/successful_default.exp7
-rw-r--r--tests/main/create-key/successful_non_default.exp7
-rw-r--r--tests/main/create-key/task.yaml27
-rw-r--r--tests/main/op-remove-retry/task.yaml2
-rw-r--r--tests/main/writable-areas/task.yaml16
-rw-r--r--tests/manual-tests.md (renamed from integration-tests/manual-tests.md)0
16 files changed, 423 insertions, 1081 deletions
diff --git a/.gitignore b/.gitignore
index 572bba7ac3..c075f9adab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,3 @@
-integration-tests/bin/
-integration-tests/data/output/
share
tags
.coverage
diff --git a/asserts/gpgkeypairmgr.go b/asserts/gpgkeypairmgr.go
index ccf5f74b3a..9024975172 100644
--- a/asserts/gpgkeypairmgr.go
+++ b/asserts/gpgkeypairmgr.go
@@ -83,6 +83,18 @@ func runGPGImpl(input []byte, args ...string) ([]byte, error) {
return nil, err
}
+ // Ensure the gpg-agent knows what tty to talk to to ask for
+ // the passphrase. This is needed because we drive gpg over
+ // a pipe and if the agent is not already started it will
+ // fail to be able to ask for a password.
+ if os.Getenv("GPG_TTY") == "" {
+ tty, err := os.Readlink("/proc/self/fd/0")
+ if err != nil {
+ return nil, err
+ }
+ os.Setenv("GPG_TTY", tty)
+ }
+
general := []string{"--homedir", homedir, "-q", "--no-auto-check-trustdb"}
allArgs := append(general, args...)
diff --git a/cmd/snap/cmd_sign_test.go b/cmd/snap/cmd_sign_test.go
index fa797542e7..341551e7e7 100644
--- a/cmd/snap/cmd_sign_test.go
+++ b/cmd/snap/cmd_sign_test.go
@@ -1,5 +1,4 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
-// +build !integrationcoverage
/*
* Copyright (C) 2016 Canonical Ltd
diff --git a/debian/rules b/debian/rules
index 140b80de6f..ecc1743e52 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,7 +5,7 @@
export DH_OPTIONS
export DH_GOPKG := github.com/snapcore/snapd
#export DEB_BUILD_OPTIONS=nocheck
-export DH_GOLANG_EXCLUDES=integration-tests tests
+export DH_GOLANG_EXCLUDES=tests
export DH_GOLANG_GO_GENERATE=1
export PATH:=${PATH}:${CURDIR}
diff --git a/overlord/auth/auth.go b/overlord/auth/auth.go
index 2f1fa46749..ad99a566a1 100644
--- a/overlord/auth/auth.go
+++ b/overlord/auth/auth.go
@@ -228,15 +228,17 @@ type DeviceAssertions interface {
}
var (
+ // ErrNoSerial indicates that a device serial is not set yet.
ErrNoSerial = errors.New("no device serial yet")
)
// An AuthContext exposes authorization data and handles its updates.
type AuthContext interface {
Device() (*DeviceState, error)
- UpdateDevice(device *DeviceState) error
- UpdateUser(user *UserState) error
+ UpdateDeviceAuth(device *DeviceState, sessionMacaroon string) (actual *DeviceState, err error)
+
+ UpdateUserAuth(user *UserState, discharges []string) (actual *UserState, err error)
StoreID(fallback string) (string, error)
@@ -265,20 +267,46 @@ func (ac *authContext) Device() (*DeviceState, error) {
return Device(ac.state)
}
-// UpdateDevice updates device in state.
-func (ac *authContext) UpdateDevice(device *DeviceState) error {
+// UpdateDeviceAuth updates the device auth details in state.
+// The last update wins but other device details are left unchanged.
+// It returns the updated device state value.
+func (ac *authContext) UpdateDeviceAuth(device *DeviceState, newSessionMacaroon string) (actual *DeviceState, err error) {
ac.state.Lock()
defer ac.state.Unlock()
- return SetDevice(ac.state, device)
+ cur, err := Device(ac.state)
+ if err != nil {
+ return nil, err
+ }
+
+ // just do it, last update wins
+ cur.SessionMacaroon = newSessionMacaroon
+ if err := SetDevice(ac.state, cur); err != nil {
+ return nil, fmt.Errorf("internal error: cannot update just read device state: %v", err)
+ }
+
+ return cur, nil
}
-// UpdateUser updates user in state.
-func (ac *authContext) UpdateUser(user *UserState) error {
+// UpdateUserAuth updates the user auth details in state.
+// The last update wins but other user details are left unchanged.
+// It returns the updated user state value.
+func (ac *authContext) UpdateUserAuth(user *UserState, newDischarges []string) (actual *UserState, err error) {
ac.state.Lock()
defer ac.state.Unlock()
- return UpdateUser(ac.state, user)
+ cur, err := User(ac.state, user.ID)
+ if err != nil {
+ return nil, err
+ }
+
+ // just do it, last update wins
+ cur.StoreDischarges = newDischarges
+ if err := UpdateUser(ac.state, cur); err != nil {
+ return nil, fmt.Errorf("internal error: cannot update just read user state: %v", err)
+ }
+
+ return cur, nil
}
// StoreID returns the store id according to system state or
diff --git a/overlord/auth/auth_test.go b/overlord/auth/auth_test.go
index 6b0d5ffdd6..4a44107c56 100644
--- a/overlord/auth/auth_test.go
+++ b/overlord/auth/auth_test.go
@@ -283,16 +283,15 @@ func (as *authSuite) TestSetDevice(c *C) {
c.Check(device, DeepEquals, &auth.DeviceState{Brand: "some-brand"})
}
-func (as *authSuite) TestAuthContextUpdateUser(c *C) {
+func (as *authSuite) TestAuthContextUpdateUserAuth(c *C) {
as.state.Lock()
user, _ := auth.NewUser(as.state, "username", "macaroon", []string{"discharge"})
as.state.Unlock()
- user.Username = "different"
- user.StoreDischarges = []string{"updated-discharge"}
+ newDischarges := []string{"updated-discharge"}
authContext := auth.NewAuthContext(as.state, nil)
- err := authContext.UpdateUser(user)
+ user, err := authContext.UpdateUserAuth(user, newDischarges)
c.Check(err, IsNil)
as.state.Lock()
@@ -300,9 +299,43 @@ func (as *authSuite) TestAuthContextUpdateUser(c *C) {
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) 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: "username",
+ Macaroon: "macaroon2",
+ Discharges: []string{"discharge"},
+ StoreMacaroon: "macaroon",
+ StoreDischarges: newDischarges,
+ })
}
-func (as *authSuite) TestAuthContextUpdateUserInvalid(c *C) {
+func (as *authSuite) TestAuthContextUpdateUserAuthInvalid(c *C) {
as.state.Lock()
_, _ = auth.NewUser(as.state, "username", "macaroon", []string{"discharge"})
as.state.Unlock()
@@ -314,7 +347,7 @@ func (as *authSuite) TestAuthContextUpdateUserInvalid(c *C) {
}
authContext := auth.NewAuthContext(as.state, nil)
- err := authContext.UpdateUser(user)
+ _, err := authContext.UpdateUserAuth(user, nil)
c.Assert(err, ErrorMatches, "invalid user")
}
@@ -340,21 +373,50 @@ func (as *authSuite) TestAuthContextDevice(c *C) {
c.Check(deviceFromState, DeepEquals, device)
}
-func (as *authSuite) TestAuthContextUpdateDevice(c *C) {
+func (as *authSuite) TestAuthContextUpdateDeviceAuth(c *C) {
as.state.Lock()
device, err := auth.Device(as.state)
as.state.Unlock()
c.Check(err, IsNil)
c.Check(device, DeepEquals, &auth.DeviceState{})
+ sessionMacaroon := "the-device-macaroon"
+
authContext := auth.NewAuthContext(as.state, nil)
- device.SessionMacaroon = "the-device-macaroon"
- err = authContext.UpdateDevice(device)
+ device, err = authContext.UpdateDeviceAuth(device, sessionMacaroon)
c.Check(err, IsNil)
deviceFromState, err := authContext.Device()
c.Check(err, IsNil)
c.Check(deviceFromState, DeepEquals, device)
+ c.Check(deviceFromState.SessionMacaroon, DeepEquals, sessionMacaroon)
+}
+
+func (as *authSuite) TestAuthContextUpdateDeviceAuthOtherUpdate(c *C) {
+ as.state.Lock()
+ device, _ := auth.Device(as.state)
+ otherUpdateDevice := *device
+ otherUpdateDevice.SessionMacaroon = "othe-session-macaroon"
+ otherUpdateDevice.KeyID = "KEYID"
+ err := auth.SetDevice(as.state, &otherUpdateDevice)
+ as.state.Unlock()
+ c.Check(err, IsNil)
+
+ sessionMacaroon := "the-device-macaroon"
+
+ authContext := auth.NewAuthContext(as.state, nil)
+ curDevice, err := authContext.UpdateDeviceAuth(device, sessionMacaroon)
+ c.Assert(err, IsNil)
+
+ as.state.Lock()
+ deviceFromState, err := auth.Device(as.state)
+ as.state.Unlock()
+ c.Check(err, IsNil)
+ c.Check(deviceFromState, DeepEquals, curDevice)
+ c.Check(curDevice, DeepEquals, &auth.DeviceState{
+ KeyID: "KEYID",
+ SessionMacaroon: sessionMacaroon,
+ })
}
func (as *authSuite) TestAuthContextStoreIDFallback(c *C) {
diff --git a/run-checks b/run-checks
index e89dd5b7b7..0e75ee457d 100755
--- a/run-checks
+++ b/run-checks
@@ -30,7 +30,7 @@ case "${1:-all}" in
SPREAD=1
;;
*)
- echo "Wrong flag ${1}. To run a single suite use --static, --unit, --spread, or --integration."
+ echo "Wrong flag ${1}. To run a single suite use --static, --unit, --spread."
exit 1
esac
@@ -116,12 +116,12 @@ if [ "$UNIT" = 1 ]; then
echo "mode: set" > .coverage/coverage.out
echo Building
- go build -tags=excludeintegration -v github.com/snapcore/snapd/...
+ go build -v github.com/snapcore/snapd/...
# tests
echo Running tests from $(pwd)
- for pkg in $(go list ./... | grep -v integration-tests); do
- $goctest -tags=excludeintegration -v -coverprofile=.coverage/profile.out $pkg
+ for pkg in $(go list ./...); do
+ $goctest -v -coverprofile=.coverage/profile.out $pkg
append_coverage .coverage/profile.out
done
diff --git a/store/store.go b/store/store.go
index 2eaa7c1cb8..9a715a649a 100644
--- a/store/store.go
+++ b/store/store.go
@@ -367,52 +367,53 @@ func authenticateUser(r *http.Request, user *auth.UserState) {
r.Header.Set("Authorization", buf.String())
}
-// refreshMacaroon will request a refreshed discharge macaroon for the user
-func refreshMacaroon(user *auth.UserState) error {
+// refreshDischarges will request refreshed discharge macaroons for the user
+func refreshDischarges(user *auth.UserState) ([]string, error) {
+ newDischarges := make([]string, len(user.StoreDischarges))
for i, d := range user.StoreDischarges {
discharge, err := MacaroonDeserialize(d)
if err != nil {
- return err
+ return nil, err
}
- if discharge.Location() == UbuntuoneLocation {
- refreshedDischarge, err := RefreshDischargeMacaroon(d)
- if err != nil {
- return err
- }
- user.StoreDischarges[i] = refreshedDischarge
+ if discharge.Location() != UbuntuoneLocation {
+ newDischarges[i] = d
+ continue
}
+
+ refreshedDischarge, err := RefreshDischargeMacaroon(d)
+ if err != nil {
+ return nil, err
+ }
+ newDischarges[i] = refreshedDischarge
}
- return nil
+ return newDischarges, nil
}
// refreshUser will refresh user discharge macaroon and update state
func (s *Store) refreshUser(user *auth.UserState) error {
- err := refreshMacaroon(user)
+ newDischarges, err := refreshDischarges(user)
if err != nil {
return err
}
if s.authContext != nil {
- err = s.authContext.UpdateUser(user)
+ curUser, err := s.authContext.UpdateUserAuth(user, newDischarges)
if err != nil {
return err
}
+ // update in place
+ *user = *curUser
}
return nil
}
// refreshDeviceSession will set or refresh the device session in the state
-func (s *Store) refreshDeviceSession() error {
+func (s *Store) refreshDeviceSession(device *auth.DeviceState) error {
if s.authContext == nil {
return fmt.Errorf("internal error: no authContext")
}
- device, err := s.authContext.Device()
- if err != nil {
- return err
- }
-
nonce, err := RequestStoreDeviceNonce()
if err != nil {
return err
@@ -428,11 +429,12 @@ func (s *Store) refreshDeviceSession() error {
return err
}
- device.SessionMacaroon = session
- err = s.authContext.UpdateDevice(device)
+ curDevice, err := s.authContext.UpdateDeviceAuth(device, session)
if err != nil {
return err
}
+ // update in place
+ *device = *curDevice
return nil
}
@@ -492,7 +494,15 @@ func (s *Store) doRequest(client *http.Client, reqOptions *requestOptions, user
}
if strings.Contains(wwwAuth, "refresh_device_session=1") {
// refresh device session
- err = s.refreshDeviceSession()
+ if s.authContext == nil {
+ return nil, fmt.Errorf("internal error: no authContext")
+ }
+ device, err := s.authContext.Device()
+ if err != nil {
+ return nil, err
+ }
+
+ err = s.refreshDeviceSession(device)
if err != nil {
return nil, err
}
@@ -526,8 +536,10 @@ func (s *Store) newRequest(reqOptions *requestOptions, user *auth.UserState) (*h
if err != nil {
return nil, err
}
- if device.SessionMacaroon == "" {
- err = s.refreshDeviceSession()
+ // we don't have a session yet but have a serial, try
+ // to get a session
+ if device.SessionMacaroon == "" && device.Serial != "" {
+ err = s.refreshDeviceSession(device)
if err == auth.ErrNoSerial {
// missing serial assertion, log and continue without device authentication
logger.Debugf("cannot set device session: %v", err)
diff --git a/store/store_test.go b/store/store_test.go
index 19cfe11d34..90f539b3e0 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -102,17 +102,23 @@ type testAuthContext struct {
}
func (ac *testAuthContext) Device() (*auth.DeviceState, error) {
- return ac.device, nil
+ freshDevice := *ac.device
+ return &freshDevice, nil
}
-func (ac *testAuthContext) UpdateDevice(d *auth.DeviceState) error {
- ac.device = d
- return nil
+func (ac *testAuthContext) UpdateDeviceAuth(d *auth.DeviceState, newSessionMacaroon string) (*auth.DeviceState, error) {
+ ac.c.Assert(d, DeepEquals, ac.device)
+ updated := *ac.device
+ updated.SessionMacaroon = newSessionMacaroon
+ *ac.device = updated
+ return &updated, nil
}
-func (ac *testAuthContext) UpdateUser(u *auth.UserState) error {
+func (ac *testAuthContext) UpdateUserAuth(u *auth.UserState, newDischarges []string) (*auth.UserState, error) {
ac.c.Assert(u, DeepEquals, ac.user)
- return nil
+ updated := *ac.user
+ updated.StoreDischarges = newDischarges
+ return &updated, nil
}
func (ac *testAuthContext) StoreID(fallback string) (string, error) {
@@ -199,6 +205,7 @@ func createTestDevice() *auth.DeviceState {
return &auth.DeviceState{
Brand: "some-brand",
SessionMacaroon: "device-macaroon",
+ Serial: "9999",
}
}
@@ -336,6 +343,180 @@ func (t *remoteRepoTestSuite) TestDownloadSyncFails(c *C) {
c.Assert(osutil.FileExists(tmpfile.Name()), Equals, false)
}
+func (t *remoteRepoTestSuite) TestDoRequestSetsAuth(c *C) {
+ mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ c.Check(r.UserAgent(), Equals, userAgent)
+ // check user authorization is set
+ authorization := r.Header.Get("Authorization")
+ c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
+ // check device authorization is set
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
+ io.WriteString(w, "response-data")
+ }))
+
+ c.Assert(mockServer, NotNil)
+ defer mockServer.Close()
+
+ authContext := &testAuthContext{c: c, device: t.device, user: t.user}
+ repo := New(&Config{}, authContext)
+ c.Assert(repo, NotNil)
+
+ endpoint, _ := url.Parse(mockServer.URL)
+ reqOptions := &requestOptions{Method: "GET", URL: endpoint}
+
+ response, err := repo.doRequest(repo.client, reqOptions, t.user)
+ defer response.Body.Close()
+ c.Assert(err, IsNil)
+
+ responseData, err := ioutil.ReadAll(response.Body)
+ c.Assert(err, IsNil)
+ c.Check(string(responseData), Equals, "response-data")
+}
+
+func (t *remoteRepoTestSuite) TestDoRequestAuthNoSerial(c *C) {
+ mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ c.Check(r.UserAgent(), Equals, userAgent)
+ // check user authorization is set
+ authorization := r.Header.Get("Authorization")
+ c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
+ // check device authorization was not set
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, "")
+
+ io.WriteString(w, "response-data")
+ }))
+
+ c.Assert(mockServer, NotNil)
+ defer mockServer.Close()
+
+ // no serial and no device macaroon => no device auth
+ t.device.Serial = ""
+ t.device.SessionMacaroon = ""
+ authContext := &testAuthContext{c: c, device: t.device, user: t.user}
+ repo := New(&Config{}, authContext)
+ c.Assert(repo, NotNil)
+
+ endpoint, _ := url.Parse(mockServer.URL)
+ reqOptions := &requestOptions{Method: "GET", URL: endpoint}
+
+ response, err := repo.doRequest(repo.client, reqOptions, t.user)
+ defer response.Body.Close()
+ c.Assert(err, IsNil)
+
+ responseData, err := ioutil.ReadAll(response.Body)
+ c.Assert(err, IsNil)
+ c.Check(string(responseData), Equals, "response-data")
+}
+
+func (t *remoteRepoTestSuite) TestDoRequestRefreshesAuth(c *C) {
+ refresh, err := makeTestRefreshDischargeResponse()
+ c.Assert(err, IsNil)
+ c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
+
+ // mock refresh response
+ refreshDischargeEndpointHit := false
+ mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
+ refreshDischargeEndpointHit = true
+ }))
+ defer mockSSOServer.Close()
+ UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
+
+ // mock store response (requiring auth refresh)
+ mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ c.Check(r.UserAgent(), Equals, userAgent)
+
+ authorization := r.Header.Get("Authorization")
+ c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
+ if t.user.StoreDischarges[0] == refresh {
+ io.WriteString(w, "response-data")
+ } else {
+ w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
+ w.WriteHeader(http.StatusUnauthorized)
+ }
+ }))
+ c.Assert(mockServer, NotNil)
+ defer mockServer.Close()
+
+ authContext := &testAuthContext{c: c, device: t.device, user: t.user}
+ repo := New(&Config{}, authContext)
+ c.Assert(repo, NotNil)
+
+ endpoint, _ := url.Parse(mockServer.URL)
+ reqOptions := &requestOptions{Method: "GET", URL: endpoint}
+
+ response, err := repo.doRequest(repo.client, reqOptions, t.user)
+ defer response.Body.Close()
+ c.Assert(err, IsNil)
+
+ responseData, err := ioutil.ReadAll(response.Body)
+ c.Assert(err, IsNil)
+ c.Check(string(responseData), Equals, "response-data")
+ c.Check(refreshDischargeEndpointHit, Equals, true)
+}
+
+func (t *remoteRepoTestSuite) TestDoRequestSetsAndRefreshesDeviceAuth(c *C) {
+ deviceSessionRequested := false
+ refreshSessionRequested := false
+ expiredAuth := `Macaroon root="expired-session-macaroon"`
+ // mock store response
+ mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ c.Check(r.UserAgent(), Equals, userAgent)
+
+ switch r.URL.Path {
+ case "/":
+ authorization := r.Header.Get("X-Device-Authorization")
+ if authorization == "" {
+ c.Fatalf("device authentication missing")
+ } else if authorization == expiredAuth {
+ w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
+ w.WriteHeader(http.StatusUnauthorized)
+ } else {
+ c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
+ io.WriteString(w, "response-data")
+ }
+ case "/identity/api/v1/nonces":
+ io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
+ case "/identity/api/v1/sessions":
+ authorization := r.Header.Get("X-Device-Authorization")
+ if authorization == "" {
+ io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
+ deviceSessionRequested = true
+ } else {
+ c.Check(authorization, Equals, expiredAuth)
+ io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
+ refreshSessionRequested = true
+ }
+ default:
+ c.Fatalf("unexpected path %q", r.URL.Path)
+ }
+ }))
+ c.Assert(mockServer, NotNil)
+ defer mockServer.Close()
+
+ MyAppsDeviceNonceAPI = mockServer.URL + "/identity/api/v1/nonces"
+ MyAppsDeviceSessionAPI = mockServer.URL + "/identity/api/v1/sessions"
+
+ // make sure device session is not set
+ t.device.SessionMacaroon = ""
+ authContext := &testAuthContext{c: c, device: t.device, user: t.user}
+ repo := New(&Config{}, authContext)
+ c.Assert(repo, NotNil)
+
+ endpoint, _ := url.Parse(mockServer.URL)
+ reqOptions := &requestOptions{Method: "GET", URL: endpoint}
+
+ response, err := repo.doRequest(repo.client, reqOptions, t.user)
+ defer response.Body.Close()
+ c.Assert(err, IsNil)
+
+ responseData, err := ioutil.ReadAll(response.Body)
+ c.Assert(err, IsNil)
+ c.Check(string(responseData), Equals, "response-data")
+ c.Check(deviceSessionRequested, Equals, true)
+ c.Check(refreshSessionRequested, Equals, true)
+}
+
const (
funkyAppName = "8nzc1x4iim2xj1g2ul64"
funkyAppDeveloper = "chipaca"
@@ -469,6 +650,9 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetails(c *C) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Check(r.UserAgent(), Equals, userAgent)
+ // check device authorization is set, implicitly checking doRequest was used
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
// no store ID by default
storeID := r.Header.Get("X-Ubuntu-Store")
c.Check(storeID, Equals, "")
@@ -497,7 +681,8 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetails(c *C) {
cfg := Config{
DetailsURI: detailsURI,
}
- repo := New(&cfg, nil)
+ authContext := &testAuthContext{c: c, device: t.device}
+ repo := New(&cfg, authContext)
c.Assert(repo, NotNil)
// the actual test
@@ -593,6 +778,10 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryStoreIDFromAuthContext(c
func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryRevision(c *C) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if strings.HasPrefix(r.URL.Path, "/dev/api/snap-purchases") {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
c.Check(r.URL.Path, Equals, "/details/hello-world")
c.Check(r.URL.Query(), DeepEquals, url.Values{
"channel": []string{""},
@@ -605,11 +794,12 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryRevision(c *C) {
c.Assert(mockServer, NotNil)
defer mockServer.Close()
-
+ purchasesURI, err := url.Parse(mockServer.URL + "/dev/api/snap-purchases/")
detailsURI, err := url.Parse(mockServer.URL + "/details/")
c.Assert(err, IsNil)
cfg := DefaultConfig()
cfg.DetailsURI = detailsURI
+ cfg.PurchasesURI = purchasesURI
repo := New(cfg, nil)
c.Assert(repo, NotNil)
@@ -662,177 +852,6 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetailsDevmode(c *C) {
c.Check(snap.Validate(result), IsNil)
}
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetailsSetsAuth(c *C) {
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // check user authorization is set
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- // check device authorization is set
- c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
- c.Check(r.UserAgent(), Equals, userAgent)
-
- io.WriteString(w, MockDetailsJSON)
- }))
-
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
-
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
- // check user authorization is set
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- // check device authorization is set
- c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
-
- c.Check(r.URL.Path, Equals, "/dev/api/snap-purchases/"+helloWorldSnapID+"/")
- c.Check(r.URL.Query().Get("include_item_purchases"), Equals, "true")
- io.WriteString(w, mockPurchaseJSON)
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
-
- detailsURI, err := url.Parse(mockServer.URL + "/details/")
- c.Assert(err, IsNil)
- purchasesURI, err := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
- c.Assert(err, IsNil)
-
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- DetailsURI: detailsURI,
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- snap, err := repo.Snap("hello-world", "edge", false, snap.R(0), t.user)
- c.Assert(snap, NotNil)
- c.Assert(err, IsNil)
- c.Check(snap.MustBuy, Equals, false)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetailsRefreshesAuth(c *C) {
- refresh, err := makeTestRefreshDischargeResponse()
- c.Assert(err, IsNil)
- c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
-
- // mock refresh response
- refreshDischargeEndpointHit := false
- mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
- refreshDischargeEndpointHit = true
- }))
- defer mockSSOServer.Close()
- UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
-
- // mock purchases response
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, mockPurchaseJSON)
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- purchasesURI, _ := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
-
- // mock store response (requiring auth refresh)
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- if t.user.StoreDischarges[0] == refresh {
- io.WriteString(w, MockDetailsJSON)
- } else {
- w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
- w.WriteHeader(http.StatusUnauthorized)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
- detailsURI, _ := url.Parse(mockServer.URL + "/details/")
-
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- DetailsURI: detailsURI,
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- snap, err := repo.Snap("hello-world", "edge", false, snap.R(0), t.user)
- c.Assert(err, IsNil)
- c.Check(refreshDischargeEndpointHit, Equals, true)
- c.Assert(snap, NotNil)
- c.Check(snap.MustBuy, Equals, false)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetailsSetsAndRefreshesDeviceAuth(c *C) {
- // mock purchases response
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, mockPurchaseJSON)
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- purchasesURI, _ := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
-
- deviceSessionRequested := false
- refreshSessionRequested := false
- expiredAuth := `Macaroon root="expired-session-macaroon"`
- // mock store response
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- switch r.URL.Path {
- case "/details/hello-world":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- c.Fatalf("device authentication missing")
- } else if authorization == expiredAuth {
- w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
- w.WriteHeader(http.StatusUnauthorized)
- } else {
- c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
- io.WriteString(w, MockDetailsJSON)
- }
- case "/identity/api/v1/nonces":
- io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
- case "/identity/api/v1/sessions":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
- deviceSessionRequested = true
- } else {
- c.Check(authorization, Equals, expiredAuth)
- io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
- refreshSessionRequested = true
- }
- default:
- c.Fatalf("unexpected path %q", r.URL.Path)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
- MyAppsDeviceNonceAPI = mockServer.URL + "/identity/api/v1/nonces"
- MyAppsDeviceSessionAPI = mockServer.URL + "/identity/api/v1/sessions"
- detailsURI, _ := url.Parse(mockServer.URL + "/details/")
-
- // make sure device session is not set
- t.device.SessionMacaroon = ""
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- DetailsURI: detailsURI,
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- snap, err := repo.Snap("hello-world", "edge", false, snap.R(0), t.user)
- c.Assert(err, IsNil)
- c.Assert(snap, NotNil)
- c.Check(deviceSessionRequested, Equals, true)
- c.Check(refreshSessionRequested, Equals, true)
- c.Check(snap.MustBuy, Equals, false)
-}
-
func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetailsOopses(c *C) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Check(r.URL.Path, Equals, "/details/hello-world")
@@ -975,6 +994,9 @@ const MockSearchJSON = `{
func (t *remoteRepoTestSuite) TestUbuntuStoreFindQueries(c *C) {
n := 0
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // check device authorization is set, implicitly checking doRequest was used
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
query := r.URL.Query()
name := query.Get("name")
@@ -1005,7 +1027,8 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFindQueries(c *C) {
DetailsURI: detailsURI,
SearchURI: searchURI,
}
- repo := New(&cfg, nil)
+ authContext := &testAuthContext{c: c, device: t.device}
+ repo := New(&cfg, authContext)
c.Assert(repo, NotNil)
for _, query := range []Search{
@@ -1143,193 +1166,6 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFindBadBody(c *C) {
c.Check(snaps, HasLen, 0)
}
-func (t *remoteRepoTestSuite) TestUbuntuStoreFindSetsAuth(c *C) {
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
- // check user authorization is set
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- // check device authorization is set
- c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
-
- c.Check(r.URL.Query().Get("q"), Equals, "foo")
- w.Header().Set("Content-Type", "application/hal+json")
- io.WriteString(w, MockSearchJSON)
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
-
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- // check device authorization is set
- c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
- c.Check(r.URL.Path, Equals, "/dev/api/snap-purchases/"+helloWorldSnapID+"/")
- c.Check(r.URL.Query().Get("include_item_purchases"), Equals, "true")
- io.WriteString(w, mockPurchaseJSON)
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
-
- var err error
- searchURI, err := url.Parse(mockServer.URL)
- c.Assert(err, IsNil)
- purchasesURI, err := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
- c.Assert(err, IsNil)
-
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- SearchURI: searchURI,
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- snaps, err := repo.Find(&Search{Query: "foo"}, t.user)
- c.Assert(err, IsNil)
- c.Assert(snaps, HasLen, 1)
- c.Check(snaps[0].SnapID, Equals, helloWorldSnapID)
- c.Check(snaps[0].Prices, DeepEquals, map[string]float64{"EUR": 2.99, "USD": 3.49})
- c.Check(snaps[0].MustBuy, Equals, false)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreFindRefreshesAuth(c *C) {
- refresh, err := makeTestRefreshDischargeResponse()
- c.Assert(err, IsNil)
- c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
-
- // mock refresh response
- refreshDischargeEndpointHit := false
- mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
- refreshDischargeEndpointHit = true
- }))
- defer mockSSOServer.Close()
- UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
-
- // mock purchases response
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
- io.WriteString(w, mockPurchaseJSON)
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- purchasesURI, err := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
- c.Assert(err, IsNil)
-
- // mock store response (requiring auth refresh)
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- if t.user.StoreDischarges[0] == refresh {
- c.Check(r.URL.Query().Get("q"), Equals, "foo")
- w.Header().Set("Content-Type", "application/hal+json")
- w.WriteHeader(http.StatusOK)
- io.WriteString(w, MockSearchJSON)
- } else {
- w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
- w.WriteHeader(http.StatusUnauthorized)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
-
- searchURI, _ := url.Parse(mockServer.URL)
- cfg := Config{
- SearchURI: searchURI,
- PurchasesURI: purchasesURI,
- }
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
-
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- snaps, err := repo.Find(&Search{Query: "foo"}, t.user)
- c.Assert(err, IsNil)
- c.Check(refreshDischargeEndpointHit, Equals, true)
- c.Assert(snaps, HasLen, 1)
- c.Check(snaps[0].SnapID, Equals, helloWorldSnapID)
- c.Check(snaps[0].Prices, DeepEquals, map[string]float64{"EUR": 2.99, "USD": 3.49})
- c.Check(snaps[0].MustBuy, Equals, false)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreFindSetsAndRefreshesDeviceAuth(c *C) {
- // mock purchases response
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
- io.WriteString(w, mockPurchaseJSON)
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- purchasesURI, err := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
- c.Assert(err, IsNil)
-
- deviceSessionRequested := false
- refreshSessionRequested := false
- expiredAuth := `Macaroon root="expired-session-macaroon"`
- // mock store response
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- switch r.URL.Path {
- case "/":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- c.Fatalf("device authentication missing")
- } else if authorization == expiredAuth {
- w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
- w.WriteHeader(http.StatusUnauthorized)
- } else {
- c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
- c.Check(r.URL.Query().Get("q"), Equals, "foo")
- w.Header().Set("Content-Type", "application/hal+json")
- w.WriteHeader(http.StatusOK)
- io.WriteString(w, MockSearchJSON)
- }
- case "/identity/api/v1/nonces":
- io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
- case "/identity/api/v1/sessions":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
- deviceSessionRequested = true
- } else {
- c.Check(authorization, Equals, expiredAuth)
- io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
- refreshSessionRequested = true
- }
- default:
- c.Fatalf("unexpected path %q", r.URL.Path)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
- MyAppsDeviceNonceAPI = mockServer.URL + "/identity/api/v1/nonces"
- MyAppsDeviceSessionAPI = mockServer.URL + "/identity/api/v1/sessions"
- searchURI, _ := url.Parse(mockServer.URL)
-
- cfg := Config{
- SearchURI: searchURI,
- PurchasesURI: purchasesURI,
- }
- // make sure device session is not set
- t.device.SessionMacaroon = ""
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
-
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- snaps, err := repo.Find(&Search{Query: "foo"}, t.user)
- c.Assert(err, IsNil)
- c.Check(deviceSessionRequested, Equals, true)
- c.Check(refreshSessionRequested, Equals, true)
- c.Assert(snaps, HasLen, 1)
- c.Check(snaps[0].SnapID, Equals, helloWorldSnapID)
- c.Check(snaps[0].Prices, DeepEquals, map[string]float64{"EUR": 2.99, "USD": 3.49})
- c.Check(snaps[0].MustBuy, Equals, false)
-}
-
func (t *remoteRepoTestSuite) TestUbuntuStoreFindAuthFailed(c *C) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// check authorization is set
@@ -1436,6 +1272,9 @@ var MockUpdatesJSON = `
func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryListRefresh(c *C) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // check device authorization is set, implicitly checking doRequest was used
+ c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
jsonReq, err := ioutil.ReadAll(r.Body)
c.Assert(err, IsNil)
var resp struct {
@@ -1468,7 +1307,8 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryListRefresh(c *C) {
cfg := Config{
BulkURI: bulkURI,
}
- repo := New(&cfg, nil)
+ authContext := &testAuthContext{c: c, device: t.device}
+ repo := New(&cfg, authContext)
c.Assert(repo, NotNil)
results, err := repo.ListRefresh([]*RefreshCandidate{
@@ -1633,159 +1473,6 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryUpdateNotSendLocalRevs(c
c.Assert(err, IsNil)
}
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryUpdatesSetsAuth(c *C) {
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // check user authorization is set
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- // check device authorization is set
- c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
-
- io.WriteString(w, MockUpdatesJSON)
- }))
-
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
-
- var err error
- bulkURI, err := url.Parse(mockServer.URL + "/updates/")
- c.Assert(err, IsNil)
-
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- BulkURI: bulkURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- _, err = repo.ListRefresh([]*RefreshCandidate{
- {
- SnapID: helloWorldSnapID,
- Channel: "stable",
- Revision: snap.R(1),
- Epoch: "0",
- DevMode: false,
- },
- }, t.user)
- c.Assert(err, IsNil)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryUpdatesRefreshesAuth(c *C) {
- refresh, err := makeTestRefreshDischargeResponse()
- c.Assert(err, IsNil)
- c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
-
- // mock refresh response
- refreshDischargeEndpointHit := false
- mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
- refreshDischargeEndpointHit = true
- }))
- defer mockSSOServer.Close()
- UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
-
- // mock store response (requiring auth refresh)
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- if t.user.StoreDischarges[0] == refresh {
- io.WriteString(w, MockUpdatesJSON)
- } else {
- w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
- w.WriteHeader(http.StatusUnauthorized)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
- bulkURI, _ := url.Parse(mockServer.URL + "/updates/")
-
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- BulkURI: bulkURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- _, err = repo.ListRefresh([]*RefreshCandidate{
- {
- SnapID: helloWorldSnapID,
- Channel: "stable",
- Revision: snap.R(1),
- Epoch: "0",
- DevMode: false,
- },
- }, t.user)
- c.Assert(err, IsNil)
- c.Check(refreshDischargeEndpointHit, Equals, true)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryUpdatesSetsAndRefreshesDeviceAuth(c *C) {
- deviceSessionRequested := false
- refreshSessionRequested := false
- expiredAuth := `Macaroon root="expired-session-macaroon"`
- // mock store response
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- switch r.URL.Path {
- case "/updates/":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- c.Fatalf("device authentication missing")
- } else if authorization == expiredAuth {
- w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
- w.WriteHeader(http.StatusUnauthorized)
- } else {
- c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
- io.WriteString(w, MockUpdatesJSON)
- }
- case "/identity/api/v1/nonces":
- io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
- case "/identity/api/v1/sessions":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
- deviceSessionRequested = true
- } else {
- c.Check(authorization, Equals, expiredAuth)
- io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
- refreshSessionRequested = true
- }
- default:
- c.Fatalf("unexpected path %q", r.URL.Path)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
- MyAppsDeviceNonceAPI = mockServer.URL + "/identity/api/v1/nonces"
- MyAppsDeviceSessionAPI = mockServer.URL + "/identity/api/v1/sessions"
- bulkURI, _ := url.Parse(mockServer.URL + "/updates/")
-
- // make sure device session is not set
- t.device.SessionMacaroon = ""
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- BulkURI: bulkURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- _, err := repo.ListRefresh([]*RefreshCandidate{
- {
- SnapID: helloWorldSnapID,
- Channel: "stable",
- Revision: snap.R(1),
- Epoch: "0",
- DevMode: false,
- },
- }, t.user)
- c.Assert(err, IsNil)
- c.Check(deviceSessionRequested, Equals, true)
- c.Check(refreshSessionRequested, Equals, true)
-}
-
func (t *remoteRepoTestSuite) TestStructFieldsSurvivesNoTag(c *C) {
type s struct {
Foo int `json:"hello"`
@@ -1873,34 +1560,7 @@ xS4u9rVT6UY=`
func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertion(c *C) {
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion")
- c.Check(r.URL.Path, Equals, "/assertions/snap-declaration/16/snapidfoo")
- io.WriteString(w, testAssertion)
- }))
-
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
-
- var err error
- assertionsURI, err := url.Parse(mockServer.URL + "/assertions/")
- c.Assert(err, IsNil)
- cfg := Config{
- AssertionsURI: assertionsURI,
- }
- repo := New(&cfg, nil)
-
- a, err := repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil)
- c.Assert(err, IsNil)
- c.Check(a, NotNil)
- c.Check(a.Type(), Equals, asserts.SnapDeclarationType)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertionSetsAuth(c *C) {
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // check user authorization is set
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- // check device authorization is set
+ // check device authorization is set, implicitly checking doRequest was used
c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion")
@@ -1915,115 +1575,16 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertionSetsAuth(c *C) {
assertionsURI, err := url.Parse(mockServer.URL + "/assertions/")
c.Assert(err, IsNil)
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- AssertionsURI: assertionsURI,
- }
- repo := New(&cfg, authContext)
-
- _, err = repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, t.user)
- c.Assert(err, IsNil)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertionRefreshesAuth(c *C) {
- refresh, err := makeTestRefreshDischargeResponse()
- c.Assert(err, IsNil)
- c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
-
- // mock refresh response
- refreshDischargeEndpointHit := false
- mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
- refreshDischargeEndpointHit = true
- }))
- defer mockSSOServer.Close()
- UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
-
- // mock store response (requiring auth refresh)
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- if t.user.StoreDischarges[0] == refresh {
- c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion")
- c.Check(r.URL.Path, Equals, "/assertions/snap-declaration/16/snapidfoo")
- io.WriteString(w, testAssertion)
- } else {
- w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
- w.WriteHeader(http.StatusUnauthorized)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
- assertionsURI, _ := url.Parse(mockServer.URL + "/assertions/")
-
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- AssertionsURI: assertionsURI,
- }
- repo := New(&cfg, authContext)
-
- _, err = repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, t.user)
- c.Assert(err, IsNil)
- c.Check(refreshDischargeEndpointHit, Equals, true)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertionSetsAndRefreshesDeviceAuth(c *C) {
- deviceSessionRequested := false
- refreshSessionRequested := false
- expiredAuth := `Macaroon root="expired-session-macaroon"`
- // mock store response
- mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- switch r.URL.Path {
- case "/assertions/snap-declaration/16/snapidfoo":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- c.Fatalf("device authentication missing")
- } else if authorization == expiredAuth {
- w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
- w.WriteHeader(http.StatusUnauthorized)
- } else {
- c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
- c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion")
- io.WriteString(w, testAssertion)
- }
- case "/identity/api/v1/nonces":
- io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
- case "/identity/api/v1/sessions":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
- deviceSessionRequested = true
- } else {
- c.Check(authorization, Equals, expiredAuth)
- io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
- refreshSessionRequested = true
- }
- default:
- c.Fatalf("unexpected path %q", r.URL.Path)
- }
- }))
- c.Assert(mockServer, NotNil)
- defer mockServer.Close()
- MyAppsDeviceNonceAPI = mockServer.URL + "/identity/api/v1/nonces"
- MyAppsDeviceSessionAPI = mockServer.URL + "/identity/api/v1/sessions"
- assertionsURI, _ := url.Parse(mockServer.URL + "/assertions/")
-
- // make sure device session is not set
- t.device.SessionMacaroon = ""
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
cfg := Config{
AssertionsURI: assertionsURI,
}
+ authContext := &testAuthContext{c: c, device: t.device}
repo := New(&cfg, authContext)
- _, err := repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, t.user)
+ a, err := repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil)
c.Assert(err, IsNil)
- c.Check(deviceSessionRequested, Equals, true)
- c.Check(refreshSessionRequested, Equals, true)
+ c.Check(a, NotNil)
+ c.Check(a.Type(), Equals, asserts.SnapDeclarationType)
}
func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryNotFound(c *C) {
@@ -2096,8 +1657,10 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositorySuggestedCurrency(c *C) {
func (t *remoteRepoTestSuite) TestUbuntuStoreDecoratePurchases(c *C) {
mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
+ // check device authorization is set, implicitly checking doRequest was used
c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
+ c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
c.Check(r.URL.Path, Equals, "/dev/api/snap-purchases/")
io.WriteString(w, mockPurchasesJSON)
}))
@@ -2142,147 +1705,6 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreDecoratePurchases(c *C) {
c.Check(otherApp2.MustBuy, Equals, false)
}
-func (t *remoteRepoTestSuite) TestUbuntuStoreDecoratePurchasesRefreshesAuth(c *C) {
- refresh, err := makeTestRefreshDischargeResponse()
- c.Assert(err, IsNil)
- c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
-
- // mock refresh response
- refreshDischargeEndpointHit := false
- mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
- refreshDischargeEndpointHit = true
- }))
- defer mockSSOServer.Close()
- UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
-
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- if t.user.StoreDischarges[0] == refresh {
- io.WriteString(w, mockPurchasesJSON)
- } else {
- w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
- w.WriteHeader(http.StatusUnauthorized)
- }
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- purchasesURI, _ := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
-
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- helloWorld := &snap.Info{}
- helloWorld.SnapID = helloWorldSnapID
- helloWorld.Prices = map[string]float64{"USD": 1.23}
-
- funkyApp := &snap.Info{}
- funkyApp.SnapID = funkyAppSnapID
- funkyApp.Prices = map[string]float64{"USD": 2.34}
-
- otherApp := &snap.Info{}
- otherApp.SnapID = "other"
- otherApp.Prices = map[string]float64{"USD": 3.45}
-
- otherApp2 := &snap.Info{}
- otherApp2.SnapID = "other2"
-
- snaps := []*snap.Info{helloWorld, funkyApp, otherApp, otherApp2}
-
- err = repo.decoratePurchases(snaps, "edge", t.user)
- c.Assert(err, IsNil)
- c.Check(refreshDischargeEndpointHit, Equals, true)
-
- c.Check(helloWorld.MustBuy, Equals, false)
- c.Check(funkyApp.MustBuy, Equals, false)
- c.Check(otherApp.MustBuy, Equals, true)
- c.Check(otherApp2.MustBuy, Equals, false)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreDecoratePurchasesSetsAndRefreshesDeviceAuth(c *C) {
- deviceSessionRequested := false
- refreshSessionRequested := false
- expiredAuth := `Macaroon root="expired-session-macaroon"`
- // mock store response
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- switch r.URL.Path {
- case "/dev/api/snap-purchases/":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- c.Fatalf("device authentication missing")
- } else if authorization == expiredAuth {
- w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
- w.WriteHeader(http.StatusUnauthorized)
- } else {
- c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
- io.WriteString(w, mockPurchasesJSON)
- }
- case "/identity/api/v1/nonces":
- io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
- case "/identity/api/v1/sessions":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
- deviceSessionRequested = true
- } else {
- c.Check(authorization, Equals, expiredAuth)
- io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
- refreshSessionRequested = true
- }
- default:
- c.Fatalf("unexpected path %q", r.URL.Path)
- }
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- MyAppsDeviceNonceAPI = mockPurchasesServer.URL + "/identity/api/v1/nonces"
- MyAppsDeviceSessionAPI = mockPurchasesServer.URL + "/identity/api/v1/sessions"
- purchasesURI, _ := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
-
- // make sure device session is not set
- t.device.SessionMacaroon = ""
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- helloWorld := &snap.Info{}
- helloWorld.SnapID = helloWorldSnapID
- helloWorld.Prices = map[string]float64{"USD": 1.23}
-
- funkyApp := &snap.Info{}
- funkyApp.SnapID = funkyAppSnapID
- funkyApp.Prices = map[string]float64{"USD": 2.34}
-
- otherApp := &snap.Info{}
- otherApp.SnapID = "other"
- otherApp.Prices = map[string]float64{"USD": 3.45}
-
- otherApp2 := &snap.Info{}
- otherApp2.SnapID = "other2"
-
- snaps := []*snap.Info{helloWorld, funkyApp, otherApp, otherApp2}
-
- err := repo.decoratePurchases(snaps, "edge", t.user)
- c.Assert(err, IsNil)
- c.Check(deviceSessionRequested, Equals, true)
- c.Check(refreshSessionRequested, Equals, true)
-
- c.Check(helloWorld.MustBuy, Equals, false)
- c.Check(funkyApp.MustBuy, Equals, false)
- c.Check(otherApp.MustBuy, Equals, true)
- c.Check(otherApp2.MustBuy, Equals, false)
-}
-
func (t *remoteRepoTestSuite) TestUbuntuStoreDecoratePurchasesFailedAccess(c *C) {
mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
@@ -2559,15 +1981,19 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreBuySuccess(c *C) {
mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
- c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
+ // check device authorization is set, implicitly checking doRequest was used
c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
+ c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
c.Check(r.URL.Path, Equals, "/dev/api/snap-purchases/"+helloWorldSnapID+"/")
c.Check(r.URL.Query().Get("include_item_purchases"), Equals, "true")
io.WriteString(w, "{}")
purchaseServerGetCalled = true
case "POST":
- c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
+ // check device authorization is set, implicitly checking doRequest was used
c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
+ c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
c.Check(r.Header.Get("Content-Type"), Equals, "application/json")
c.Check(r.URL.Path, Equals, "/dev/api/snap-purchases/")
jsonReq, err := ioutil.ReadAll(r.Body)
@@ -2700,123 +2126,6 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreBuyInteractive(c *C) {
c.Check(purchaseServerPostCalled, Equals, true)
}
-func (t *remoteRepoTestSuite) TestUbuntuStoreBuyRefreshesAuth(c *C) {
- refresh, err := makeTestRefreshDischargeResponse()
- c.Assert(err, IsNil)
- c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
-
- // mock refresh response
- refreshDischargeEndpointHit := false
- mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
- refreshDischargeEndpointHit = true
- }))
- defer mockSSOServer.Close()
- UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
-
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- if t.user.StoreDischarges[0] == refresh {
- io.WriteString(w, mockSinglePurchaseJSON)
- } else {
- w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
- w.WriteHeader(http.StatusUnauthorized)
- }
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
-
- purchasesURI, _ := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
- cfg := Config{
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, nil)
- c.Assert(repo, NotNil)
-
- result, err := repo.Buy(&BuyOptions{
- SnapID: helloWorldSnapID,
- SnapName: "hello-world",
- Currency: "EUR",
- Price: 0.99,
-
- BackendID: "123",
- MethodID: 234,
- }, t.user)
-
- c.Assert(result, NotNil)
- c.Check(result.State, Equals, "Complete")
- c.Check(refreshDischargeEndpointHit, Equals, true)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStoreBuySetsAndRefreshesDeviceAuth(c *C) {
- deviceSessionRequested := false
- refreshSessionRequested := false
- expiredAuth := `Macaroon root="expired-session-macaroon"`
- // mock store response
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- switch r.URL.Path {
- case "/dev/api/snap-purchases/":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- c.Fatalf("device authentication missing")
- } else if authorization == expiredAuth {
- w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
- w.WriteHeader(http.StatusUnauthorized)
- } else {
- c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
- io.WriteString(w, mockSinglePurchaseJSON)
- }
- case "/identity/api/v1/nonces":
- io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
- case "/identity/api/v1/sessions":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
- deviceSessionRequested = true
- } else {
- c.Check(authorization, Equals, expiredAuth)
- io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
- refreshSessionRequested = true
- }
- default:
- c.Fatalf("unexpected path %q", r.URL.Path)
- }
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- MyAppsDeviceNonceAPI = mockPurchasesServer.URL + "/identity/api/v1/nonces"
- MyAppsDeviceSessionAPI = mockPurchasesServer.URL + "/identity/api/v1/sessions"
- purchasesURI, _ := url.Parse(mockPurchasesServer.URL + "/dev/api/snap-purchases/")
-
- // make sure device session is not set
- t.device.SessionMacaroon = ""
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- PurchasesURI: purchasesURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- result, err := repo.Buy(&BuyOptions{
- SnapID: helloWorldSnapID,
- SnapName: "hello-world",
- Currency: "EUR",
- Price: 0.99,
-
- BackendID: "123",
- MethodID: 234,
- }, t.user)
-
- c.Assert(err, IsNil)
- c.Assert(result, NotNil)
- c.Check(result.State, Equals, "Complete")
- c.Check(deviceSessionRequested, Equals, true)
- c.Check(refreshSessionRequested, Equals, true)
-}
-
func (t *remoteRepoTestSuite) TestUbuntuStoreBuyFailWrongPrice(c *C) {
searchServerCalled := false
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -3075,8 +2384,10 @@ func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethods(c *C) {
mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
- c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
+ // check device authorization is set, implicitly checking doRequest was used
c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`)
+
+ c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user))
c.Check(r.URL.Path, Equals, "/api/2.0/click/paymentmethods/")
io.WriteString(w, paymentMethodsJson)
purchaseServerGetCalled++
@@ -3175,101 +2486,3 @@ func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethodsHandles401(c *C) {
c.Assert(err, NotNil)
c.Check(err.Error(), Equals, "invalid credentials")
}
-
-func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethodsRefreshesAuth(c *C) {
- refresh, err := makeTestRefreshDischargeResponse()
- c.Assert(err, IsNil)
- c.Check(t.user.StoreDischarges[0], Not(Equals), refresh)
-
- // mock refresh response
- refreshDischargeEndpointHit := false
- mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, fmt.Sprintf(`{"discharge_macaroon": "%s"}`, refresh))
- refreshDischargeEndpointHit = true
- }))
- defer mockSSOServer.Close()
- UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh"
-
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- authorization := r.Header.Get("Authorization")
- c.Check(authorization, Equals, t.expectedAuthorization(c, t.user))
- if t.user.StoreDischarges[0] == refresh {
- io.WriteString(w, paymentMethodsJson)
- } else {
- w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1")
- w.WriteHeader(http.StatusUnauthorized)
- }
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
-
- paymentMethodsURI, _ := url.Parse(mockPurchasesServer.URL + "/api/2.0/click/paymentmethods/")
- cfg := Config{
- PaymentMethodsURI: paymentMethodsURI,
- }
- repo := New(&cfg, nil)
- c.Assert(repo, NotNil)
-
- result, err := repo.PaymentMethods(t.user)
- c.Assert(err, IsNil)
- c.Assert(result, NotNil)
- c.Check(refreshDischargeEndpointHit, Equals, true)
-}
-
-func (t *remoteRepoTestSuite) TestUbuntuStorePaymentMethodsSetsAndRefreshesDeviceAuth(c *C) {
- deviceSessionRequested := false
- refreshSessionRequested := false
- expiredAuth := `Macaroon root="expired-session-macaroon"`
- // mock store response
- mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- c.Check(r.UserAgent(), Equals, userAgent)
-
- switch r.URL.Path {
- case "/api/2.0/click/paymentmethods/":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- c.Fatalf("device authentication missing")
- } else if authorization == expiredAuth {
- w.Header().Set("WWW-Authenticate", "Macaroon refresh_device_session=1")
- w.WriteHeader(http.StatusUnauthorized)
- } else {
- c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`)
- io.WriteString(w, paymentMethodsJson)
- }
- case "/identity/api/v1/nonces":
- io.WriteString(w, `{"nonce": "1234567890:9876543210"}`)
- case "/identity/api/v1/sessions":
- authorization := r.Header.Get("X-Device-Authorization")
- if authorization == "" {
- io.WriteString(w, `{"macaroon": "expired-session-macaroon"}`)
- deviceSessionRequested = true
- } else {
- c.Check(authorization, Equals, expiredAuth)
- io.WriteString(w, `{"macaroon": "refreshed-session-macaroon"}`)
- refreshSessionRequested = true
- }
- default:
- c.Fatalf("unexpected path %q", r.URL.Path)
- }
- }))
- c.Assert(mockPurchasesServer, NotNil)
- defer mockPurchasesServer.Close()
- MyAppsDeviceNonceAPI = mockPurchasesServer.URL + "/identity/api/v1/nonces"
- MyAppsDeviceSessionAPI = mockPurchasesServer.URL + "/identity/api/v1/sessions"
- paymentMethodsURI, _ := url.Parse(mockPurchasesServer.URL + "/api/2.0/click/paymentmethods/")
-
- // make sure device session is not set
- t.device.SessionMacaroon = ""
- authContext := &testAuthContext{c: c, device: t.device, user: t.user}
- cfg := Config{
- PaymentMethodsURI: paymentMethodsURI,
- }
- repo := New(&cfg, authContext)
- c.Assert(repo, NotNil)
-
- result, err := repo.PaymentMethods(t.user)
- c.Assert(err, IsNil)
- c.Assert(result, NotNil)
- c.Check(deviceSessionRequested, Equals, true)
- c.Check(refreshSessionRequested, Equals, true)
-}
diff --git a/tests/lib/fakestore/store/store_test.go b/tests/lib/fakestore/store/store_test.go
index d452766305..327fc79d48 100644
--- a/tests/lib/fakestore/store/store_test.go
+++ b/tests/lib/fakestore/store/store_test.go
@@ -1,5 +1,4 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
-// +build !excludeintegration
/*
* Copyright (C) 2014-2015 Canonical Ltd
diff --git a/tests/main/create-key/successful_default.exp b/tests/main/create-key/successful_default.exp
index 2e5b9ae2af..9f4e01ab9a 100644
--- a/tests/main/create-key/successful_default.exp
+++ b/tests/main/create-key/successful_default.exp
@@ -28,10 +28,11 @@ if {[lindex $status 3] != 0} {
spawn snap export-key --account=developer default
-expect "Enter passphrase: "
-send "pass\n"
-
+# fun!
+# gpg1 asks for a passphrase on the terminal no matter what
+# gpg2 gets the passphrase via our fake pinentry
expect {
+ "Enter passphrase: " {send "pass\n"; exp_continue}
"account-id: developer" {}
timeout { exit 1 }
eof { exit 1 }
diff --git a/tests/main/create-key/successful_non_default.exp b/tests/main/create-key/successful_non_default.exp
index 5c7f815760..452f335a12 100644
--- a/tests/main/create-key/successful_non_default.exp
+++ b/tests/main/create-key/successful_non_default.exp
@@ -28,10 +28,11 @@ if {[lindex $status 3] != 0} {
spawn snap export-key --account=developer another
-expect "Enter passphrase: "
-send "pass\n"
-
+# fun!
+# gpg1 asks for a passphrase on the terminal no matter what
+# gpg2 gets the passphrase via our fake pinentry
expect {
+ "Enter passphrase: " {send "pass\n"; exp_continue}
"account-id: developer" {}
timeout { exit 1 }
eof { exit 1 }
diff --git a/tests/main/create-key/task.yaml b/tests/main/create-key/task.yaml
index b596af5b09..a7975f6fa0 100644
--- a/tests/main/create-key/task.yaml
+++ b/tests/main/create-key/task.yaml
@@ -5,6 +5,33 @@ systems: [-ubuntu-core-16-64]
restore: |
rm -rf $HOME/.snap/gnupg
+prepare: |
+ echo "setup fake gpg pinentry environment"
+ cat > /tmp/pinentry-fake <<'EOF'
+ #!/bin/sh
+ set -e
+ echo "OK Pleased to meet you"
+ while true; do
+ read line
+ case $line in
+ GETPIN)
+ echo "D pass"
+ echo "OK"
+ ;;
+ BYE)
+ exit 0
+ ;;
+ *)
+ echo "OK I'm not very smart"
+ ;;
+ esac
+ done
+ EOF
+ chmod +x /tmp/pinentry-fake
+ mkdir -p /root/.snap/gnupg/
+ chmod 0700 /root/.snap/gnupg/
+ echo pinentry-program /tmp/pinentry-fake > /root/.snap/gnupg/gpg-agent.conf
+
execute: |
echo "Checking passphrase mismatch error"
expect -f passphrase_mismatch.exp
diff --git a/tests/main/op-remove-retry/task.yaml b/tests/main/op-remove-retry/task.yaml
index 75dcca5b2a..1c85e17451 100644
--- a/tests/main/op-remove-retry/task.yaml
+++ b/tests/main/op-remove-retry/task.yaml
@@ -41,4 +41,4 @@ execute: |
# cleanup umount blocker
systemctl stop unmount-blocker
- wait_for_service unmount-blocker inactive
+
diff --git a/tests/main/writable-areas/task.yaml b/tests/main/writable-areas/task.yaml
index 29a930c4d9..4c93e18a04 100644
--- a/tests/main/writable-areas/task.yaml
+++ b/tests/main/writable-areas/task.yaml
@@ -20,19 +20,9 @@ execute: |
echo "Waiting for data writer service to finish..."
unit="snap.data-writer.service.service"
- while true; do
- code=$(systemctl show -p ExecMainCode $unit | sed 's/.*=\([0-9]\+\)/\1/')
- # The main code will be 0 until the service is no longer running
- if [ $code -ne 0 ]; then
- systemctl status $unit || true
- status=$(systemctl show -p ExecMainStatus $unit | sed 's/.*=\([0-9]\+\)/\1/')
- if [ $status -ne 0 ]; then
- echo "Service exited $status"
- exit 1
- fi
- break
- fi
- sleep 1
+ while ! systemctl status $unit | grep -q "Writing to SNAP_USER_DATA"; do
+ systemctl status $unit || true
+ sleep 1
done
echo "Services can write to writable areas"
diff --git a/integration-tests/manual-tests.md b/tests/manual-tests.md
index 0217c30057..0217c30057 100644
--- a/integration-tests/manual-tests.md
+++ b/tests/manual-tests.md