Skip to content

Commit

Permalink
asserts,interfaces/builtin,overlord/assertstate: introduce base-decla…
Browse files Browse the repository at this point in the history
…ration (#2037)

This introduces a base-declaration assertion with the default policies (for now about interfaces) that govern all snaps.

This has the assertion definition itself, the first pass at plumbing to have the content defined in interfaces/builtin, while the forward compatible (with the idea to revision/distribute this through the store) interface to it is asserstate.BaseDeclaration.

The assertion is not used yet in any way.
  • Loading branch information
pedronis authored and niemeyer committed Sep 30, 2016
1 parent 535bbeb commit 938e6b6
Show file tree
Hide file tree
Showing 9 changed files with 527 additions and 0 deletions.
2 changes: 2 additions & 0 deletions asserts/asserts.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var (
AccountKeyType = &AssertionType{"account-key", []string{"public-key-sha3-384"}, assembleAccountKey, 0}
ModelType = &AssertionType{"model", []string{"series", "brand-id", "model"}, assembleModel, 0}
SerialType = &AssertionType{"serial", []string{"brand-id", "model", "serial"}, assembleSerial, 0}
BaseDeclarationType = &AssertionType{"base-declaration", []string{"series"}, assembleBaseDeclaration, 0}
SnapDeclarationType = &AssertionType{"snap-declaration", []string{"series", "snap-id"}, assembleSnapDeclaration, 0}
SnapBuildType = &AssertionType{"snap-build", []string{"snap-sha3-384"}, assembleSnapBuild, 0}
SnapRevisionType = &AssertionType{"snap-revision", []string{"snap-sha3-384"}, assembleSnapRevision, 0}
Expand All @@ -77,6 +78,7 @@ var typeRegistry = map[string]*AssertionType{
AccountKeyType.Name: AccountKeyType,
ModelType.Name: ModelType,
SerialType.Name: SerialType,
BaseDeclarationType.Name: BaseDeclarationType,
SnapDeclarationType.Name: SnapDeclarationType,
SnapBuildType.Name: SnapBuildType,
SnapRevisionType.Name: SnapRevisionType,
Expand Down
1 change: 1 addition & 0 deletions asserts/asserts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ func (as *assertsSuite) TestWithAuthority(c *C) {
withAuthority := []string{
"account",
"account-key",
"base-declaration",
"snap-declaration",
"snap-build",
"snap-revision",
Expand Down
21 changes: 21 additions & 0 deletions asserts/assertstest/assertstest.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,24 @@ func (ss *StoreStack) StoreAccountKey(keyID string) *asserts.AccountKey {
}
return key.(*asserts.AccountKey)
}

// MockBuiltinBaseDeclaration mocks the builtin base-declaration exposed by asserts.BuiltinBaseDeclaration.
func MockBuiltinBaseDeclaration(headers []byte) (restore func()) {
var prevHeaders []byte
decl := asserts.BuiltinBaseDeclaration()
if decl != nil {
prevHeaders, _ = decl.Signature()
}

err := asserts.InitBuiltinBaseDeclaration(headers)
if err != nil {
panic(err)
}

return func() {
err := asserts.InitBuiltinBaseDeclaration(prevHeaders)
if err != nil {
panic(err)
}
}
}
141 changes: 141 additions & 0 deletions asserts/snap_asserts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package asserts

import (
"bytes"
"crypto"
"fmt"
"time"
Expand Down Expand Up @@ -485,3 +486,143 @@ func assembleValidation(assert assertionBase) (Assertion, error) {
approvedSnapRevision: approvedSnapRevision,
}, nil
}

// BaseDeclaration holds a base-declaration assertion, declaring the
// policies (to start with interface ones) applying to all snaps of
// a series.
type BaseDeclaration struct {
assertionBase
plugRules map[string]*PlugRule
slotRules map[string]*SlotRule
timestamp time.Time
}

// Series returns the series whose snaps are governed by the declaration.
func (basedcl *BaseDeclaration) Series() string {
return basedcl.HeaderString("series")
}

// Timestamp returns the time when the base-declaration was issued.
func (basedcl *BaseDeclaration) Timestamp() time.Time {
return basedcl.timestamp
}

// PlugRule returns the plug-side rule about the given interface if one was included in the plugs stanza of the declaration, otherwise it returns nil.
func (basedcl *BaseDeclaration) PlugRule(interfaceName string) *PlugRule {
return basedcl.plugRules[interfaceName]
}

// SlotRule returns the slot-side rule about the given interface if one was included in the slots stanza of the declaration, otherwise it returns nil.
func (basedcl *BaseDeclaration) SlotRule(interfaceName string) *SlotRule {
return basedcl.slotRules[interfaceName]
}

// Implement further consistency checks.
func (basedcl *BaseDeclaration) checkConsistency(db RODatabase, acck *AccountKey) error {
// XXX: not signed or stored yet in a db, but being ready for that
if !db.IsTrustedAccount(basedcl.AuthorityID()) {
return fmt.Errorf("base-declaration assertion for series %s is not signed by a directly trusted authority: %s", basedcl.Series(), basedcl.AuthorityID())
}
return nil
}

// sanity
var _ consistencyChecker = (*BaseDeclaration)(nil)

func assembleBaseDeclaration(assert assertionBase) (Assertion, error) {
var plugRules map[string]*PlugRule
plugs, err := checkMap(assert.headers, "plugs")
if err != nil {
return nil, err
}
if plugs != nil {
plugRules = make(map[string]*PlugRule, len(plugs))
for iface, rule := range plugs {
plugRule, err := compilePlugRule(iface, rule)
if err != nil {
return nil, err
}
plugRules[iface] = plugRule
}
}

var slotRules map[string]*SlotRule
slots, err := checkMap(assert.headers, "slots")
if err != nil {
return nil, err
}
if slots != nil {
slotRules = make(map[string]*SlotRule, len(slots))
for iface, rule := range slots {
slotRule, err := compileSlotRule(iface, rule)
if err != nil {
return nil, err
}
slotRules[iface] = slotRule
}
}

timestamp, err := checkRFC3339Date(assert.headers, "timestamp")
if err != nil {
return nil, err
}

return &BaseDeclaration{
assertionBase: assert,
plugRules: plugRules,
slotRules: slotRules,
timestamp: timestamp,
}, nil
}

var builtinBaseDeclaration *BaseDeclaration

// BuiltinBaseDeclaration exposes the initialized builtin base-declaration assertion. This is used by overlord/assertstate, other code should use assertstate.BaseDeclaration.
func BuiltinBaseDeclaration() *BaseDeclaration {
return builtinBaseDeclaration
}

var (
builtinBaseDeclarationCheckOrder = []string{"type", "authority-id", "series"}
builtinBaseDeclarationExpectedHeaders = map[string]interface{}{
"type": "base-declaration",
"authority-id": "canonical",
"series": release.Series,
}
)

// InitBuiltinBaseDeclaration initializes the builtin base-declaration based on headers (or resets it if headers is nil).
func InitBuiltinBaseDeclaration(headers []byte) error {
if headers == nil {
builtinBaseDeclaration = nil
return nil
}
trimmed := bytes.TrimSpace(headers)
h, err := parseHeaders(trimmed)
if err != nil {
return err
}
for _, name := range builtinBaseDeclarationCheckOrder {
expected := builtinBaseDeclarationExpectedHeaders[name]
if h[name] != expected {
return fmt.Errorf("the builtin base-declaration %q header is not set to expected value %q", name, expected)
}
}
revision, err := checkRevision(h)
if err != nil {
return fmt.Errorf("cannot assemble the builtin-base declaration: %v", err)
}
h["timestamp"] = time.Now().UTC().Format(time.RFC3339)
a, err := assembleBaseDeclaration(assertionBase{
headers: h,
body: nil,
revision: revision,
content: trimmed,
signature: []byte("$builtin"),
})
if err != nil {
return fmt.Errorf("cannot assemble the builtin base-declaration: %v", err)
}
builtinBaseDeclaration = a.(*BaseDeclaration)
return nil
}

0 comments on commit 938e6b6

Please sign in to comment.