github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/asserts/store_asserts_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 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 "github.com/snapcore/snapd/asserts/assertstest" 31 ) 32 33 var _ = Suite(&storeSuite{}) 34 35 type storeSuite struct { 36 ts time.Time 37 tsLine string 38 validExample string 39 } 40 41 func (s *storeSuite) SetUpSuite(c *C) { 42 s.ts = time.Now().Truncate(time.Second).UTC() 43 s.tsLine = "timestamp: " + s.ts.Format(time.RFC3339) + "\n" 44 s.validExample = "type: store\n" + 45 "authority-id: canonical\n" + 46 "store: store1\n" + 47 "operator-id: op-id1\n" + 48 "url: https://store.example.com\n" + 49 "location: upstairs\n" + 50 s.tsLine + 51 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n" + 52 "\n" + 53 "AXNpZw==" 54 } 55 56 func (s *storeSuite) TestDecodeOK(c *C) { 57 a, err := asserts.Decode([]byte(s.validExample)) 58 c.Assert(err, IsNil) 59 c.Check(a.Type(), Equals, asserts.StoreType) 60 store := a.(*asserts.Store) 61 62 c.Check(store.OperatorID(), Equals, "op-id1") 63 c.Check(store.Store(), Equals, "store1") 64 c.Check(store.URL().String(), Equals, "https://store.example.com") 65 c.Check(store.Location(), Equals, "upstairs") 66 c.Check(store.Timestamp().Equal(s.ts), Equals, true) 67 c.Check(store.FriendlyStores(), HasLen, 0) 68 } 69 70 var storeErrPrefix = "assertion store: " 71 72 func (s *storeSuite) TestDecodeInvalidHeaders(c *C) { 73 tests := []struct{ original, invalid, expectedErr string }{ 74 {"store: store1\n", "", `"store" header is mandatory`}, 75 {"store: store1\n", "store: \n", `"store" header should not be empty`}, 76 {"operator-id: op-id1\n", "", `"operator-id" header is mandatory`}, 77 {"operator-id: op-id1\n", "operator-id: \n", `"operator-id" header should not be empty`}, 78 {"url: https://store.example.com\n", "url:\n - foo\n", `"url" header must be a string`}, 79 {"location: upstairs\n", "location:\n - foo\n", `"location" header must be a string`}, 80 {s.tsLine, "", `"timestamp" header is mandatory`}, 81 {s.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 82 {s.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 83 {"url: https://store.example.com\n", "friendly-stores: foo\n", `"friendly-stores" header must be a list of strings`}, 84 } 85 86 for _, test := range tests { 87 invalid := strings.Replace(s.validExample, test.original, test.invalid, 1) 88 _, err := asserts.Decode([]byte(invalid)) 89 c.Check(err, ErrorMatches, storeErrPrefix+test.expectedErr) 90 } 91 } 92 93 func (s *storeSuite) TestURLOptional(c *C) { 94 tests := []string{"", "url: \n"} 95 for _, test := range tests { 96 encoded := strings.Replace(s.validExample, "url: https://store.example.com\n", test, 1) 97 assert, err := asserts.Decode([]byte(encoded)) 98 c.Assert(err, IsNil) 99 store := assert.(*asserts.Store) 100 c.Check(store.URL(), IsNil) 101 } 102 } 103 104 func (s *storeSuite) TestURL(c *C) { 105 tests := []struct { 106 url string 107 err string 108 }{ 109 // Valid URLs. 110 {"http://example.com/", ""}, 111 {"https://example.com/", ""}, 112 {"https://example.com/some/path/", ""}, 113 {"https://example.com:443/", ""}, 114 {"https://example.com:1234/", ""}, 115 {"https://user:pass@example.com/", ""}, 116 {"https://token@example.com/", ""}, 117 118 // Invalid URLs. 119 {"://example.com", `"url" header must be a valid URL`}, 120 {"example.com", `"url" header scheme must be "https" or "http"`}, 121 {"//example.com", `"url" header scheme must be "https" or "http"`}, 122 {"ftp://example.com", `"url" header scheme must be "https" or "http"`}, 123 {"mailto:someone@example.com", `"url" header scheme must be "https" or "http"`}, 124 {"https://", `"url" header must have a host`}, 125 {"https:///", `"url" header must have a host`}, 126 {"https:///some/path", `"url" header must have a host`}, 127 {"https://example.com/?foo=bar", `"url" header must not have a query`}, 128 {"https://example.com/#fragment", `"url" header must not have a fragment`}, 129 } 130 131 for _, test := range tests { 132 encoded := strings.Replace( 133 s.validExample, "url: https://store.example.com\n", 134 fmt.Sprintf("url: %s\n", test.url), 1) 135 assert, err := asserts.Decode([]byte(encoded)) 136 if test.err != "" { 137 c.Assert(err, NotNil) 138 c.Check(err.Error(), Equals, storeErrPrefix+test.err+": "+test.url) 139 } else { 140 c.Assert(err, IsNil) 141 c.Check(assert.(*asserts.Store).URL().String(), Equals, test.url) 142 } 143 } 144 } 145 146 func (s *storeSuite) TestLocationOptional(c *C) { 147 encoded := strings.Replace(s.validExample, "location: upstairs\n", "", 1) 148 _, err := asserts.Decode([]byte(encoded)) 149 c.Check(err, IsNil) 150 } 151 152 func (s *storeSuite) TestLocation(c *C) { 153 for _, test := range []string{"foo", "bar", ""} { 154 encoded := strings.Replace( 155 s.validExample, "location: upstairs\n", 156 fmt.Sprintf("location: %s\n", test), 1) 157 assert, err := asserts.Decode([]byte(encoded)) 158 c.Assert(err, IsNil) 159 store := assert.(*asserts.Store) 160 c.Check(store.Location(), Equals, test) 161 } 162 } 163 164 func (s *storeSuite) TestCheckAuthority(c *C) { 165 storeDB, db := makeStoreAndCheckDB(c) 166 167 // Add account for operator. 168 operator := assertstest.NewAccount(storeDB, "op-id1", nil, "") 169 err := db.Add(operator) 170 c.Assert(err, IsNil) 171 172 otherDB := setup3rdPartySigning(c, "other", storeDB, db) 173 174 storeHeaders := map[string]interface{}{ 175 "store": "store1", 176 "operator-id": operator.HeaderString("account-id"), 177 "timestamp": time.Now().Format(time.RFC3339), 178 } 179 180 // store signed by some other account fails. 181 store, err := otherDB.Sign(asserts.StoreType, storeHeaders, nil, "") 182 c.Assert(err, IsNil) 183 err = db.Check(store) 184 c.Assert(err, ErrorMatches, `store assertion "store1" is not signed by a directly trusted authority: other`) 185 186 // but succeeds when signed by a trusted authority. 187 store, err = storeDB.Sign(asserts.StoreType, storeHeaders, nil, "") 188 c.Assert(err, IsNil) 189 err = db.Check(store) 190 c.Assert(err, IsNil) 191 } 192 193 func (s *storeSuite) TestFriendlyStores(c *C) { 194 encoded := strings.Replace(s.validExample, "url: https://store.example.com\n", `friendly-stores: 195 - store1 196 - store2 197 - store3 198 `, 1) 199 assert, err := asserts.Decode([]byte(encoded)) 200 c.Assert(err, IsNil) 201 store := assert.(*asserts.Store) 202 c.Check(store.URL(), IsNil) 203 c.Check(store.FriendlyStores(), DeepEquals, []string{"store1", "store2", "store3"}) 204 } 205 206 func (s *storeSuite) TestCheckOperatorAccount(c *C) { 207 storeDB, db := makeStoreAndCheckDB(c) 208 209 store, err := storeDB.Sign(asserts.StoreType, map[string]interface{}{ 210 "store": "store1", 211 "operator-id": "op-id1", 212 "timestamp": time.Now().Format(time.RFC3339), 213 }, nil, "") 214 c.Assert(err, IsNil) 215 216 // No account for operator op-id1 yet, so Check fails. 217 err = db.Check(store) 218 c.Assert(err, ErrorMatches, `store assertion "store1" does not have a matching account assertion for the operator "op-id1"`) 219 220 // Add the op-id1 account. 221 operator := assertstest.NewAccount(storeDB, "op-id1", map[string]interface{}{"account-id": "op-id1"}, "") 222 err = db.Add(operator) 223 c.Assert(err, IsNil) 224 225 // Now the operator exists so Check succeeds. 226 err = db.Check(store) 227 c.Assert(err, IsNil) 228 } 229 230 func (s *storeSuite) TestPrerequisites(c *C) { 231 assert, err := asserts.Decode([]byte(s.validExample)) 232 c.Assert(err, IsNil) 233 c.Assert(assert.Prerequisites(), DeepEquals, []*asserts.Ref{ 234 {Type: asserts.AccountType, PrimaryKey: []string{"op-id1"}}, 235 }) 236 }