gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/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 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/asserts" 30 ) 31 32 var ( 33 _ = Suite(&accountSuite{}) 34 ) 35 36 type accountSuite struct { 37 ts time.Time 38 tsLine string 39 } 40 41 func (s *accountSuite) SetUpSuite(c *C) { 42 s.ts = time.Now().Truncate(time.Second).UTC() 43 s.tsLine = "timestamp: " + s.ts.Format(time.RFC3339) + "\n" 44 } 45 46 const accountExample = "type: account\n" + 47 "authority-id: canonical\n" + 48 "account-id: abc-123\n" + 49 "display-name: Nice User\n" + 50 "username: nice\n" + 51 "validation: verified\n" + 52 "TSLINE" + 53 "body-length: 0\n" + 54 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 55 "\n\n" + 56 "AXNpZw==" 57 58 func (s *accountSuite) TestDecodeOK(c *C) { 59 encoded := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 60 a, err := asserts.Decode([]byte(encoded)) 61 c.Assert(err, IsNil) 62 c.Check(a.Type(), Equals, asserts.AccountType) 63 account := a.(*asserts.Account) 64 c.Check(account.AuthorityID(), Equals, "canonical") 65 c.Check(account.Timestamp(), Equals, s.ts) 66 c.Check(account.AccountID(), Equals, "abc-123") 67 c.Check(account.DisplayName(), Equals, "Nice User") 68 c.Check(account.Username(), Equals, "nice") 69 c.Check(account.Validation(), Equals, "verified") 70 } 71 72 func (s *accountSuite) TestOptional(c *C) { 73 encoded := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 74 75 tests := []struct{ original, replacement string }{ 76 {"username: nice\n", ""}, 77 {"username: nice\n", "username: \n"}, 78 } 79 80 for _, test := range tests { 81 valid := strings.Replace(encoded, test.original, test.replacement, 1) 82 _, err := asserts.Decode([]byte(valid)) 83 c.Check(err, IsNil) 84 } 85 } 86 87 func (s *accountSuite) TestValidation(c *C) { 88 tests := []struct { 89 value string 90 isVerified bool 91 }{ 92 {"certified", true}, // backward compat for hard-coded trusted assertions 93 {"verified", true}, 94 {"unproven", false}, 95 {"nonsense", false}, 96 } 97 98 template := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 99 for _, test := range tests { 100 encoded := strings.Replace( 101 template, 102 "validation: verified\n", 103 fmt.Sprintf("validation: %s\n", test.value), 104 1, 105 ) 106 assert, err := asserts.Decode([]byte(encoded)) 107 c.Assert(err, IsNil) 108 account := assert.(*asserts.Account) 109 expected := test.value 110 if test.isVerified { 111 expected = "verified" 112 } 113 c.Check(account.Validation(), Equals, expected) 114 } 115 } 116 117 const ( 118 accountErrPrefix = "assertion account: " 119 ) 120 121 func (s *accountSuite) TestDecodeInvalid(c *C) { 122 encoded := strings.Replace(accountExample, "TSLINE", s.tsLine, 1) 123 124 invalidTests := []struct{ original, invalid, expectedErr string }{ 125 {"account-id: abc-123\n", "", `"account-id" header is mandatory`}, 126 {"account-id: abc-123\n", "account-id: \n", `"account-id" header should not be empty`}, 127 {"display-name: Nice User\n", "", `"display-name" header is mandatory`}, 128 {"display-name: Nice User\n", "display-name: \n", `"display-name" header should not be empty`}, 129 {"username: nice\n", "username:\n - foo\n - bar\n", `"username" header must be a string`}, 130 {"validation: verified\n", "", `"validation" header is mandatory`}, 131 {"validation: verified\n", "validation: \n", `"validation" header should not be empty`}, 132 {s.tsLine, "", `"timestamp" header is mandatory`}, 133 {s.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 134 {s.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 135 } 136 137 for _, test := range invalidTests { 138 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 139 _, err := asserts.Decode([]byte(invalid)) 140 c.Check(err, ErrorMatches, accountErrPrefix+test.expectedErr) 141 } 142 } 143 144 func (s *accountSuite) TestCheckInconsistentTimestamp(c *C) { 145 ex, err := asserts.Decode([]byte(strings.Replace(accountExample, "TSLINE", s.tsLine, 1))) 146 c.Assert(err, IsNil) 147 148 storeDB, db := makeStoreAndCheckDB(c) 149 150 headers := ex.Headers() 151 headers["timestamp"] = "2011-01-01T14:00:00Z" 152 account, err := storeDB.Sign(asserts.AccountType, headers, nil, "") 153 c.Assert(err, IsNil) 154 155 err = db.Check(account) 156 c.Assert(err, ErrorMatches, `account assertion timestamp "2011-01-01 14:00:00 \+0000 UTC" outside of signing key validity \(key valid since.*\)`) 157 } 158 159 func (s *accountSuite) TestCheckUntrustedAuthority(c *C) { 160 ex, err := asserts.Decode([]byte(strings.Replace(accountExample, "TSLINE", s.tsLine, 1))) 161 c.Assert(err, IsNil) 162 163 storeDB, db := makeStoreAndCheckDB(c) 164 otherDB := setup3rdPartySigning(c, "other", storeDB, db) 165 166 headers := ex.Headers() 167 // default to signing db's authority 168 delete(headers, "authority-id") 169 headers["timestamp"] = time.Now().Format(time.RFC3339) 170 account, err := otherDB.Sign(asserts.AccountType, headers, nil, "") 171 c.Assert(err, IsNil) 172 173 err = db.Check(account) 174 c.Assert(err, ErrorMatches, `account assertion for "abc-123" is not signed by a directly trusted authority:.*`) 175 } 176 177 func (s *accountSuite) TestIsValidAccountID(c *C) { 178 ids := []struct { 179 accountID string 180 valid bool 181 }{ 182 {"f", false}, 183 {"", false}, 184 {"foo", true}, 185 {"foo-baraaaaaaaaaaaaaaaaaaaaa", true}, // 28 characters 186 {"foo-baraaaaaaaaaaaaaaaaaaaaax", false}, // too long 187 {"fooBAR9aaaaaaaaaaaaaaaaaaaaaaaaa", true}, // 32 characters 188 {"fooBAR9aaaaaaaaaaaaaaaaaaaaaaaaax", false}, // too long 189 {"foo-bar12", true}, 190 {"-foo-bar", true}, 191 } 192 193 for i, id := range ids { 194 c.Assert(asserts.IsValidAccountID(id.accountID), Equals, id.valid, Commentf("%d: %s", i, id.accountID)) 195 } 196 }