github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/asserts/account_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package asserts_test 21 22 import ( 23 "fmt" 24 "strings" 25 "time" 26 27 "github.com/snapcore/snapd/asserts" 28 . "gopkg.in/check.v1" 29 ) 30 31 var ( 32 _ = Suite(&accountSuite{}) 33 ) 34 35 type accountSuite struct { 36 ts time.Time 37 tsLine string 38 } 39 40 func (s *accountSuite) SetUpSuite(c *C) { 41 s.ts = time.Now().Truncate(time.Second).UTC() 42 s.tsLine = "timestamp: " + s.ts.Format(time.RFC3339) + "\n" 43 } 44 45 const accountExample = "type: account\n" + 46 "authority-id: canonical\n" + 47 "account-id: abc-123\n" + 48 "display-name: Nice User\n" + 49 "username: nice\n" + 50 "validation: verified\n" + 51 "TSLINE" + 52 "body-length: 0\n" + 53 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 54 "\n\n" + 55 "AXNpZw==" 56 57 func (s *accountSuite) TestDecodeOK(c *C) { 58 encoded := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 59 a, err := asserts.Decode([]byte(encoded)) 60 c.Assert(err, IsNil) 61 c.Check(a.Type(), Equals, asserts.AccountType) 62 account := a.(*asserts.Account) 63 c.Check(account.AuthorityID(), Equals, "canonical") 64 c.Check(account.Timestamp(), Equals, s.ts) 65 c.Check(account.AccountID(), Equals, "abc-123") 66 c.Check(account.DisplayName(), Equals, "Nice User") 67 c.Check(account.Username(), Equals, "nice") 68 c.Check(account.Validation(), Equals, "verified") 69 } 70 71 func (s *accountSuite) TestOptional(c *C) { 72 encoded := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 73 74 tests := []struct{ original, replacement string }{ 75 {"username: nice\n", ""}, 76 {"username: nice\n", "username: \n"}, 77 } 78 79 for _, test := range tests { 80 valid := strings.Replace(encoded, test.original, test.replacement, 1) 81 _, err := asserts.Decode([]byte(valid)) 82 c.Check(err, IsNil) 83 } 84 } 85 86 func (s *accountSuite) TestValidation(c *C) { 87 tests := []struct { 88 value string 89 isVerified bool 90 }{ 91 {"certified", true}, // backward compat for hard-coded trusted assertions 92 {"verified", true}, 93 {"unproven", false}, 94 {"nonsense", false}, 95 } 96 97 template := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 98 for _, test := range tests { 99 encoded := strings.Replace( 100 template, 101 "validation: verified\n", 102 fmt.Sprintf("validation: %s\n", test.value), 103 1, 104 ) 105 assert, err := asserts.Decode([]byte(encoded)) 106 c.Assert(err, IsNil) 107 account := assert.(*asserts.Account) 108 expected := test.value 109 if test.isVerified { 110 expected = "verified" 111 } 112 c.Check(account.Validation(), Equals, expected) 113 } 114 } 115 116 const ( 117 accountErrPrefix = "assertion account: " 118 ) 119 120 func (s *accountSuite) TestDecodeInvalid(c *C) { 121 encoded := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 122 123 invalidTests := []struct{ original, invalid, expectedErr string }{ 124 {"account-id: abc-123\n", "", `"account-id" header is mandatory`}, 125 {"account-id: abc-123\n", "account-id: \n", `"account-id" header should not be empty`}, 126 {"display-name: Nice User\n", "", `"display-name" header is mandatory`}, 127 {"display-name: Nice User\n", "display-name: \n", `"display-name" header should not be empty`}, 128 {"username: nice\n", "username:\n - foo\n - bar\n", `"username" header must be a string`}, 129 {"validation: verified\n", "", `"validation" header is mandatory`}, 130 {"validation: verified\n", "validation: \n", `"validation" header should not be empty`}, 131 {s.tsLine, "", `"timestamp" header is mandatory`}, 132 {s.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 133 {s.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 134 } 135 136 for _, test := range invalidTests { 137 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 138 _, err := asserts.Decode([]byte(invalid)) 139 c.Check(err, ErrorMatches, accountErrPrefix+test.expectedErr) 140 } 141 } 142 143 func (s *accountSuite) TestCheckInconsistentTimestamp(c *C) { 144 ex, err := asserts.Decode([]byte(strings.Replace(accountExample, "TSLINE", s.tsLine, 1))) 145 c.Assert(err, IsNil) 146 147 storeDB, db := makeStoreAndCheckDB(c) 148 149 headers := ex.Headers() 150 headers["timestamp"] = "2011-01-01T14:00:00Z" 151 account, err := storeDB.Sign(asserts.AccountType, headers, nil, "") 152 c.Assert(err, IsNil) 153 154 err = db.Check(account) 155 c.Assert(err, ErrorMatches, `account assertion timestamp outside of signing key validity \(key valid since.*\)`) 156 } 157 158 func (s *accountSuite) TestCheckUntrustedAuthority(c *C) { 159 ex, err := asserts.Decode([]byte(strings.Replace(accountExample, "TSLINE", s.tsLine, 1))) 160 c.Assert(err, IsNil) 161 162 storeDB, db := makeStoreAndCheckDB(c) 163 otherDB := setup3rdPartySigning(c, "other", storeDB, db) 164 165 headers := ex.Headers() 166 // default to signing db's authority 167 delete(headers, "authority-id") 168 headers["timestamp"] = time.Now().Format(time.RFC3339) 169 account, err := otherDB.Sign(asserts.AccountType, headers, nil, "") 170 c.Assert(err, IsNil) 171 172 err = db.Check(account) 173 c.Assert(err, ErrorMatches, `account assertion for "abc-123" is not signed by a directly trusted authority:.*`) 174 }