Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into HEAD
Browse files Browse the repository at this point in the history
  • Loading branch information
mvo5 committed Aug 22, 2017
2 parents 15759c5 + 42e1124 commit 3e65cc5
Show file tree
Hide file tree
Showing 61 changed files with 2,108 additions and 473 deletions.
2 changes: 2 additions & 0 deletions asserts/asserts.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var (
SnapDeveloperType = &AssertionType{"snap-developer", []string{"snap-id", "publisher-id"}, assembleSnapDeveloper, 0}
SystemUserType = &AssertionType{"system-user", []string{"brand-id", "email"}, assembleSystemUser, 0}
ValidationType = &AssertionType{"validation", []string{"series", "snap-id", "approved-snap-id", "approved-snap-revision"}, assembleValidation, 0}
StoreType = &AssertionType{"store", []string{"store"}, assembleStore, 0}

// ...
)
Expand All @@ -92,6 +93,7 @@ var typeRegistry = map[string]*AssertionType{
SystemUserType.Name: SystemUserType,
ValidationType.Name: ValidationType,
RepairType.Name: RepairType,
StoreType.Name: StoreType,
// no authority
DeviceSessionRequestType.Name: DeviceSessionRequestType,
SerialRequestType.Name: SerialRequestType,
Expand Down
2 changes: 2 additions & 0 deletions asserts/asserts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func (as *assertsSuite) TestTypeNames(c *C) {
"snap-declaration",
"snap-developer",
"snap-revision",
"store",
"system-user",
"test-only",
"test-only-2",
Expand Down Expand Up @@ -844,6 +845,7 @@ func (as *assertsSuite) TestWithAuthority(c *C) {
"account",
"account-key",
"base-declaration",
"store",
"snap-declaration",
"snap-build",
"snap-revision",
Expand Down
52 changes: 35 additions & 17 deletions asserts/assertstest/assertstest.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ func NewSigningDB(authorityID string, privKey asserts.PrivateKey) *SigningDB {

func (db *SigningDB) Sign(assertType *asserts.AssertionType, headers map[string]interface{}, body []byte, keyID string) (asserts.Assertion, error) {
if _, ok := headers["authority-id"]; !ok {
// copy before modifying
headers2 := make(map[string]interface{}, len(headers)+1)
for h, v := range headers {
headers2[h] = v
}
headers = headers2
headers["authority-id"] = db.AuthorityID
}
if keyID == "" {
Expand Down Expand Up @@ -272,21 +278,33 @@ type StoreStack struct {
*SigningDB
}

// NewStoreStack creates a new store assertion stack. It panics on error.
// optional privKeys can be in order: root, store, generic
func NewStoreStack(authorityID string, privKeys ...asserts.PrivateKey) *StoreStack {
if len(privKeys) > 3 {
panic("too many private keys specified, expected at most: root, store, generic")
// StoreKeys holds a set of store private keys.
type StoreKeys struct {
Root asserts.PrivateKey
Store asserts.PrivateKey
Generic asserts.PrivateKey
}

var (
rootPrivKey, _ = GenerateKey(1024)
storePrivKey, _ = GenerateKey(752)
genericPrivKey, _ = GenerateKey(752)

pregenKeys = StoreKeys{
Root: rootPrivKey,
Store: storePrivKey,
Generic: genericPrivKey,
}
for len(privKeys) < 3 {
privKey, _ := GenerateKey(752)
privKeys = append(privKeys, privKey)
)

// NewStoreStack creates a new store assertion stack. It panics on error.
// Optional keys specify private keys to use for the various roles.
func NewStoreStack(authorityID string, keys *StoreKeys) *StoreStack {
if keys == nil {
keys = &pregenKeys
}
rootPrivKey := privKeys[0]
storePrivKey := privKeys[1]
genericPrivKey := privKeys[2]

rootSigning := NewSigningDB(authorityID, rootPrivKey)
rootSigning := NewSigningDB(authorityID, keys.Root)
ts := time.Now().Format(time.RFC3339)
trustedAcct := NewAccount(rootSigning, authorityID, map[string]interface{}{
"account-id": authorityID,
Expand All @@ -296,7 +314,7 @@ func NewStoreStack(authorityID string, privKeys ...asserts.PrivateKey) *StoreSta
trustedKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{
"name": "root",
"since": ts,
}, rootPrivKey.PublicKey(), "")
}, keys.Root.PublicKey(), "")
trusted := []asserts.Assertion{trustedAcct, trustedKey}

genericAcct := NewAccount(rootSigning, "generic", map[string]interface{}{
Expand All @@ -314,26 +332,26 @@ func NewStoreStack(authorityID string, privKeys ...asserts.PrivateKey) *StoreSta
if err != nil {
panic(err)
}
err = db.ImportKey(storePrivKey)
err = db.ImportKey(keys.Store)
if err != nil {
panic(err)
}
storeKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{
"name": "store",
}, storePrivKey.PublicKey(), "")
}, keys.Store.PublicKey(), "")
err = db.Add(storeKey)
if err != nil {
panic(err)
}

err = db.ImportKey(genericPrivKey)
err = db.ImportKey(keys.Generic)
if err != nil {
panic(err)
}
genericKey := NewAccountKey(rootSigning, genericAcct, map[string]interface{}{
"name": "serials",
"since": ts,
}, genericPrivKey.PublicKey(), "")
}, keys.Generic.PublicKey(), "")
err = db.Add(genericKey)
if err != nil {
panic(err)
Expand Down
5 changes: 1 addition & 4 deletions asserts/assertstest/assertstest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ func (s *helperSuite) TestReadPrivKeyUnarmored(c *C) {
}

func (s *helperSuite) TestStoreStack(c *C) {
rootPrivKey, _ := assertstest.GenerateKey(1024)
storePrivKey, _ := assertstest.GenerateKey(752)

store := assertstest.NewStoreStack("super", rootPrivKey, storePrivKey)
store := assertstest.NewStoreStack("super", nil)

c.Check(store.TrustedAccount.AccountID(), Equals, "super")
c.Check(store.TrustedAccount.IsCertified(), Equals, true)
Expand Down
4 changes: 1 addition & 3 deletions asserts/fetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ type fetcherSuite struct {
var _ = Suite(&fetcherSuite{})

func (s *fetcherSuite) SetUpTest(c *C) {
rootPrivKey, _ := assertstest.GenerateKey(1024)
storePrivKey, _ := assertstest.GenerateKey(752)
s.storeSigning = assertstest.NewStoreStack("can0nical", rootPrivKey, storePrivKey)
s.storeSigning = assertstest.NewStoreStack("can0nical", nil)
}

func fakeSnap(rev int) []byte {
Expand Down
2 changes: 1 addition & 1 deletion asserts/gpgkeypairmgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (gkms *gpgKeypairMgrSuite) TestGetNotFound(c *C) {
}

func (gkms *gpgKeypairMgrSuite) TestUseInSigning(c *C) {
store := assertstest.NewStoreStack("trusted", testPrivKey0, testPrivKey1)
store := assertstest.NewStoreStack("trusted", nil)

devKey, err := gkms.keypairMgr.Get(assertstest.DevKeyID)
c.Assert(err, IsNil)
Expand Down
5 changes: 1 addition & 4 deletions asserts/snap_asserts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,10 +596,7 @@ func (sbs *snapBuildSuite) TestDecodeInvalid(c *C) {
}

func makeStoreAndCheckDB(c *C) (store *assertstest.StoreStack, checkDB *asserts.Database) {
trustedPrivKey := testPrivKey0
storePrivKey := testPrivKey1

store = assertstest.NewStoreStack("canonical", trustedPrivKey, storePrivKey)
store = assertstest.NewStoreStack("canonical", nil)
cfg := &asserts.DatabaseConfig{
Backstore: asserts.NewMemoryBackstore(),
Trusted: store.Trusted,
Expand Down
4 changes: 1 addition & 3 deletions asserts/snapasserts/snapasserts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ type snapassertsSuite struct {
var _ = Suite(&snapassertsSuite{})

func (s *snapassertsSuite) SetUpTest(c *C) {
rootPrivKey, _ := assertstest.GenerateKey(1024)
storePrivKey, _ := assertstest.GenerateKey(752)
s.storeSigning = assertstest.NewStoreStack("can0nical", rootPrivKey, storePrivKey)
s.storeSigning = assertstest.NewStoreStack("can0nical", nil)

s.dev1Acct = assertstest.NewAccount(s.storeSigning, "developer1", nil, "")

Expand Down
112 changes: 112 additions & 0 deletions asserts/store_asserts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package asserts

import (
"fmt"
"net/url"
)

// Store holds a store assertion, defining the configuration needed to connect
// a device to the store.
type Store struct {
assertionBase
url *url.URL
}

// Store returns the identifying name of the operator's store.
func (store *Store) Store() string {
return store.HeaderString("store")
}

// OperatorID returns the account id of the store's operator.
func (store *Store) OperatorID() string {
return store.HeaderString("operator-id")
}

// URL returns the URL of the store's API.
func (store *Store) URL() *url.URL {
return store.url
}

// Location returns a summary of the store's location/purpose.
func (store *Store) Location() string {
return store.HeaderString("location")
}

func (store *Store) checkConsistency(db RODatabase, acck *AccountKey) error {
// Will be applied to a system's snapd so must be signed by a trusted authority.
if !db.IsTrustedAccount(store.AuthorityID()) {
return fmt.Errorf("store assertion %q is not signed by a directly trusted authority: %s",
store.Store(), store.AuthorityID())
}

_, err := db.Find(AccountType, map[string]string{"account-id": store.OperatorID()})
if err != nil {
if err == ErrNotFound {
return fmt.Errorf(
"store assertion %q does not have a matching account assertion for the operator %q",
store.Store(), store.OperatorID())
}
return err
}

return nil
}

// Prerequisites returns references to this store's prerequisite assertions.
func (store *Store) Prerequisites() []*Ref {
return []*Ref{
{AccountType, []string{store.OperatorID()}},
}
}

// checkStoreURL validates the "url" header and returns a full URL or nil.
func checkStoreURL(headers map[string]interface{}) (*url.URL, error) {
s, err := checkOptionalString(headers, "url")
if err != nil {
return nil, err
}

if s == "" {
return nil, nil
}

errWhat := `"url" header`

u, err := url.Parse(s)
if err != nil {
return nil, fmt.Errorf("%s must be a valid URL: %s", errWhat, s)
}
if u.Scheme != "http" && u.Scheme != "https" {
return nil, fmt.Errorf(`%s scheme must be "https" or "http": %s`, errWhat, s)
}
if u.Host == "" {
return nil, fmt.Errorf(`%s must have a host: %s`, errWhat, s)
}
if u.RawQuery != "" {
return nil, fmt.Errorf(`%s must not have a query: %s`, errWhat, s)
}
if u.Fragment != "" {
return nil, fmt.Errorf(`%s must not have a fragment: %s`, errWhat, s)
}

return u, nil
}

func assembleStore(assert assertionBase) (Assertion, error) {
_, err := checkNotEmptyString(assert.headers, "operator-id")
if err != nil {
return nil, err
}

url, err := checkStoreURL(assert.headers)
if err != nil {
return nil, err
}

_, err = checkOptionalString(assert.headers, "location")
if err != nil {
return nil, err
}

return &Store{assertionBase: assert, url: url}, nil
}
Loading

0 comments on commit 3e65cc5

Please sign in to comment.