mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
The `rec.HeaderMap` is deprecated since go1.11 and the `rec.Header()` method should be used instead. This commit does this switch. Found by https://staticcheck.io/ which was suggested by Fred, thanks for this.
474 lines
15 KiB
Go
474 lines
15 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
/*
|
|
* Copyright (C) 2016-2019 Canonical Ltd
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 3 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
package daemon_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"gopkg.in/check.v1"
|
|
|
|
"github.com/snapcore/snapd/asserts"
|
|
"github.com/snapcore/snapd/asserts/assertstest"
|
|
"github.com/snapcore/snapd/daemon"
|
|
"github.com/snapcore/snapd/overlord/assertstate"
|
|
"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
|
|
"github.com/snapcore/snapd/overlord/auth"
|
|
"github.com/snapcore/snapd/testutil"
|
|
)
|
|
|
|
type assertsSuite struct {
|
|
apiBaseSuite
|
|
|
|
mockAssertionFn func(at *asserts.AssertionType, headers []string, user *auth.UserState) (asserts.Assertion, error)
|
|
}
|
|
|
|
var _ = check.Suite(&assertsSuite{})
|
|
|
|
func (s *assertsSuite) SetUpTest(c *check.C) {
|
|
s.apiBaseSuite.SetUpTest(c)
|
|
|
|
s.mockAssertionFn = nil
|
|
|
|
s.daemonWithStore(c, s)
|
|
}
|
|
|
|
func (s *assertsSuite) TestGetAsserts(c *check.C) {
|
|
req, err := http.NewRequest("GET", "/v2/assertions", nil)
|
|
c.Assert(err, check.IsNil)
|
|
resp := s.syncReq(c, req, nil)
|
|
c.Check(resp.Status, check.Equals, 200)
|
|
c.Check(resp.Result, check.DeepEquals, map[string][]string{"types": asserts.TypeNames()})
|
|
}
|
|
|
|
func (s *assertsSuite) addAsserts(assertions ...asserts.Assertion) {
|
|
st := s.d.Overlord().State()
|
|
st.Lock()
|
|
defer st.Unlock()
|
|
assertstatetest.AddMany(st, s.StoreSigning.StoreAccountKey(""))
|
|
assertstatetest.AddMany(st, assertions...)
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertOK(c *check.C) {
|
|
// add store key
|
|
s.addAsserts()
|
|
|
|
st := s.d.Overlord().State()
|
|
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
buf := bytes.NewBuffer(asserts.Encode(acct))
|
|
// Execute
|
|
req, err := http.NewRequest("POST", "/v2/assertions", buf)
|
|
c.Assert(err, check.IsNil)
|
|
rsp := s.syncReq(c, req, nil)
|
|
// Verify (external)
|
|
c.Check(rsp.Status, check.Equals, 200)
|
|
// Verify (internal)
|
|
st.Lock()
|
|
defer st.Unlock()
|
|
_, err = assertstate.DB(st).Find(asserts.AccountType, map[string]string{
|
|
"account-id": acct.AccountID(),
|
|
})
|
|
c.Check(err, check.IsNil)
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertStreamOK(c *check.C) {
|
|
st := s.d.Overlord().State()
|
|
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
buf := &bytes.Buffer{}
|
|
enc := asserts.NewEncoder(buf)
|
|
err := enc.Encode(acct)
|
|
c.Assert(err, check.IsNil)
|
|
err = enc.Encode(s.StoreSigning.StoreAccountKey(""))
|
|
c.Assert(err, check.IsNil)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("POST", "/v2/assertions", buf)
|
|
c.Assert(err, check.IsNil)
|
|
rsp := s.syncReq(c, req, nil)
|
|
// Verify (external)
|
|
c.Check(rsp.Status, check.Equals, 200)
|
|
// Verify (internal)
|
|
st.Lock()
|
|
defer st.Unlock()
|
|
_, err = assertstate.DB(st).Find(asserts.AccountType, map[string]string{
|
|
"account-id": acct.AccountID(),
|
|
})
|
|
c.Check(err, check.IsNil)
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertInvalid(c *check.C) {
|
|
// Setup
|
|
buf := bytes.NewBufferString("blargh")
|
|
req, err := http.NewRequest("POST", "/v2/assertions", buf)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
// Execute
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify (external)
|
|
c.Check(rec.Code, check.Equals, 400)
|
|
c.Check(rec.Body.String(), testutil.Contains,
|
|
"cannot decode request body into assertions")
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertError(c *check.C) {
|
|
// Setup
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
buf := bytes.NewBuffer(asserts.Encode(acct))
|
|
req, err := http.NewRequest("POST", "/v2/assertions", buf)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
// Execute
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify (external)
|
|
c.Check(rec.Code, check.Equals, 400)
|
|
c.Check(rec.Body.String(), testutil.Contains, "assert failed")
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyAll(c *check.C) {
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", map[string]interface{}{
|
|
"account-id": "developer1-id",
|
|
}, "")
|
|
s.addAsserts(acct)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/x.ubuntu.assertion; bundle=y")
|
|
c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "4")
|
|
dec := asserts.NewDecoder(rec.Body)
|
|
a1, err := dec.Decode()
|
|
c.Assert(err, check.IsNil)
|
|
c.Check(a1.Type(), check.Equals, asserts.AccountType)
|
|
|
|
a2, err := dec.Decode()
|
|
c.Assert(err, check.IsNil)
|
|
|
|
a3, err := dec.Decode()
|
|
c.Assert(err, check.IsNil)
|
|
|
|
a4, err := dec.Decode()
|
|
c.Assert(err, check.IsNil)
|
|
|
|
_, err = dec.Decode()
|
|
c.Assert(err, check.Equals, io.EOF)
|
|
|
|
ids := []string{a1.(*asserts.Account).AccountID(), a2.(*asserts.Account).AccountID(), a3.(*asserts.Account).AccountID(), a4.(*asserts.Account).AccountID()}
|
|
sort.Strings(ids)
|
|
c.Check(ids, check.DeepEquals, []string{"can0nical", "canonical", "developer1-id", "generic"})
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyFilter(c *check.C) {
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
s.addAsserts(acct)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account?username=developer1", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "1")
|
|
dec := asserts.NewDecoder(rec.Body)
|
|
a1, err := dec.Decode()
|
|
c.Assert(err, check.IsNil)
|
|
c.Check(a1.Type(), check.Equals, asserts.AccountType)
|
|
c.Check(a1.(*asserts.Account).Username(), check.Equals, "developer1")
|
|
c.Check(a1.(*asserts.Account).AccountID(), check.Equals, acct.AccountID())
|
|
_, err = dec.Decode()
|
|
c.Check(err, check.Equals, io.EOF)
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyNoResults(c *check.C) {
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
s.addAsserts(acct)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account?username=xyzzyx", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "0")
|
|
dec := asserts.NewDecoder(rec.Body)
|
|
_, err = dec.Decode()
|
|
c.Check(err, check.Equals, io.EOF)
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsInvalidType(c *check.C) {
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/foo", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 400)
|
|
c.Check(rec.Body.String(), testutil.Contains, "invalid assert type")
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyJSONFilter(c *check.C) {
|
|
s.testAssertsFindManyJSONFilter(c, "/v2/assertions/account?json=true&username=developer1")
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyJSONFilterRemoteIsFalse(c *check.C) {
|
|
// setting "remote=false" is the defalt and should not change anything
|
|
s.testAssertsFindManyJSONFilter(c, "/v2/assertions/account?json=true&username=developer1&remote=false")
|
|
}
|
|
|
|
func (s *assertsSuite) testAssertsFindManyJSONFilter(c *check.C, urlPath string) {
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
s.addAsserts(acct)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", urlPath, nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json")
|
|
|
|
var body map[string]interface{}
|
|
err = json.Unmarshal(rec.Body.Bytes(), &body)
|
|
c.Assert(err, check.IsNil)
|
|
c.Check(body["result"], check.DeepEquals, []interface{}{
|
|
map[string]interface{}{
|
|
"headers": acct.Headers(),
|
|
},
|
|
})
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyJSONNoResults(c *check.C) {
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
s.addAsserts(acct)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account?json=true&username=xyz", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json")
|
|
|
|
var body map[string]interface{}
|
|
err = json.Unmarshal(rec.Body.Bytes(), &body)
|
|
c.Assert(err, check.IsNil)
|
|
c.Check(body["result"], check.DeepEquals, []interface{}{})
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyJSONWithBody(c *check.C) {
|
|
// add store key
|
|
s.addAsserts()
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account-key?json=true", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json")
|
|
|
|
var got []string
|
|
var body map[string]interface{}
|
|
err = json.Unmarshal(rec.Body.Bytes(), &body)
|
|
c.Assert(err, check.IsNil)
|
|
for _, a := range body["result"].([]interface{}) {
|
|
h := a.(map[string]interface{})["headers"].(map[string]interface{})
|
|
got = append(got, h["account-id"].(string)+"/"+h["name"].(string))
|
|
// check body
|
|
l, err := strconv.Atoi(h["body-length"].(string))
|
|
c.Assert(err, check.IsNil)
|
|
c.Check(a.(map[string]interface{})["body"], check.HasLen, l)
|
|
}
|
|
sort.Strings(got)
|
|
c.Check(got, check.DeepEquals, []string{"can0nical/root", "can0nical/store", "canonical/root", "generic/models"})
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyJSONHeadersOnly(c *check.C) {
|
|
// add store key
|
|
s.addAsserts()
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account-key?json=headers&account-id=can0nical", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json")
|
|
|
|
var got []string
|
|
var body map[string]interface{}
|
|
err = json.Unmarshal(rec.Body.Bytes(), &body)
|
|
c.Assert(err, check.IsNil)
|
|
for _, a := range body["result"].([]interface{}) {
|
|
h := a.(map[string]interface{})["headers"].(map[string]interface{})
|
|
got = append(got, h["account-id"].(string)+"/"+h["name"].(string))
|
|
// check body absent
|
|
_, ok := a.(map[string]interface{})["body"]
|
|
c.Assert(ok, check.Equals, false)
|
|
}
|
|
sort.Strings(got)
|
|
c.Check(got, check.DeepEquals, []string{"can0nical/root", "can0nical/store"})
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyJSONInvalidParam(c *check.C) {
|
|
// add store key
|
|
s.addAsserts()
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account-key?json=header&account-id=can0nical", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 400, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json")
|
|
|
|
var rsp daemon.RespJSON
|
|
c.Assert(json.Unmarshal(rec.Body.Bytes(), &rsp), check.IsNil)
|
|
c.Check(rsp.Status, check.Equals, 400)
|
|
c.Check(rsp.Type, check.Equals, daemon.ResponseTypeError)
|
|
c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{
|
|
"message": `"json" query parameter when used must be set to "true" or "headers"`,
|
|
})
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyJSONNopFilter(c *check.C) {
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
s.addAsserts(acct)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account?json=false&username=developer1", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "1")
|
|
dec := asserts.NewDecoder(rec.Body)
|
|
a1, err := dec.Decode()
|
|
c.Assert(err, check.IsNil)
|
|
c.Check(a1.Type(), check.Equals, asserts.AccountType)
|
|
c.Check(a1.(*asserts.Account).Username(), check.Equals, "developer1")
|
|
c.Check(a1.(*asserts.Account).AccountID(), check.Equals, acct.AccountID())
|
|
_, err = dec.Decode()
|
|
c.Check(err, check.Equals, io.EOF)
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyRemoteInvalidParam(c *check.C) {
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account-key?remote=invalid&account-id=can0nical", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(rec.Code, check.Equals, 400, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json")
|
|
var rsp daemon.RespJSON
|
|
c.Assert(json.Unmarshal(rec.Body.Bytes(), &rsp), check.IsNil)
|
|
c.Check(rsp.Status, check.Equals, 400)
|
|
c.Check(rsp.Type, check.Equals, daemon.ResponseTypeError)
|
|
c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{
|
|
"message": `"remote" query parameter when used must be set to "true" or "false" or left unset`,
|
|
})
|
|
}
|
|
|
|
func (s *assertsSuite) Assertion(at *asserts.AssertionType, headers []string, user *auth.UserState) (asserts.Assertion, error) {
|
|
return s.mockAssertionFn(at, headers, user)
|
|
}
|
|
|
|
func (s *assertsSuite) TestAssertsFindManyRemote(c *check.C) {
|
|
var assertFnCalled int
|
|
s.mockAssertionFn = func(at *asserts.AssertionType, headers []string, user *auth.UserState) (asserts.Assertion, error) {
|
|
assertFnCalled++
|
|
c.Assert(at.Name, check.Equals, "account")
|
|
c.Assert(headers, check.DeepEquals, []string{"can0nical"})
|
|
return assertstest.NewAccount(s.StoreSigning, "some-developer", nil, ""), nil
|
|
}
|
|
|
|
acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "")
|
|
s.addAsserts(acct)
|
|
|
|
// Execute
|
|
req, err := http.NewRequest("GET", "/v2/assertions/account?remote=true&account-id=can0nical", nil)
|
|
c.Assert(err, check.IsNil)
|
|
s.asUserAuth(c, req)
|
|
|
|
rec := httptest.NewRecorder()
|
|
s.serveHTTP(c, rec, req)
|
|
// Verify
|
|
c.Check(assertFnCalled, check.Equals, 1)
|
|
c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body))
|
|
c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/x.ubuntu.assertion; bundle=y")
|
|
|
|
data := rec.Body.Bytes()
|
|
c.Check(string(data), check.Matches, `(?ms)type: account
|
|
authority-id: can0nical
|
|
account-id: [a-zA-Z0-9]+
|
|
display-name: Some-developer
|
|
timestamp: .*
|
|
username: some-developer
|
|
validation: unproven
|
|
.*
|
|
`)
|
|
|
|
}
|