gitee.com/mysnapcore/mysnapd@v0.1.0/daemon/api_asserts_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2019 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 daemon_test 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "io" 26 "net/http" 27 "net/http/httptest" 28 "sort" 29 "strconv" 30 31 "gopkg.in/check.v1" 32 33 "gitee.com/mysnapcore/mysnapd/asserts" 34 "gitee.com/mysnapcore/mysnapd/asserts/assertstest" 35 "gitee.com/mysnapcore/mysnapd/daemon" 36 "gitee.com/mysnapcore/mysnapd/overlord/assertstate" 37 "gitee.com/mysnapcore/mysnapd/overlord/assertstate/assertstatetest" 38 "gitee.com/mysnapcore/mysnapd/overlord/auth" 39 "gitee.com/mysnapcore/mysnapd/testutil" 40 ) 41 42 type assertsSuite struct { 43 apiBaseSuite 44 45 mockAssertionFn func(at *asserts.AssertionType, headers []string, user *auth.UserState) (asserts.Assertion, error) 46 } 47 48 var _ = check.Suite(&assertsSuite{}) 49 50 func (s *assertsSuite) SetUpTest(c *check.C) { 51 s.apiBaseSuite.SetUpTest(c) 52 53 s.mockAssertionFn = nil 54 55 s.daemonWithStore(c, s) 56 } 57 58 func (s *assertsSuite) TestGetAsserts(c *check.C) { 59 req, err := http.NewRequest("GET", "/v2/assertions", nil) 60 c.Assert(err, check.IsNil) 61 resp := s.syncReq(c, req, nil) 62 c.Check(resp.Status, check.Equals, 200) 63 c.Check(resp.Result, check.DeepEquals, map[string][]string{"types": asserts.TypeNames()}) 64 } 65 66 func (s *assertsSuite) addAsserts(assertions ...asserts.Assertion) { 67 st := s.d.Overlord().State() 68 st.Lock() 69 defer st.Unlock() 70 assertstatetest.AddMany(st, s.StoreSigning.StoreAccountKey("")) 71 assertstatetest.AddMany(st, assertions...) 72 } 73 74 func (s *assertsSuite) TestAssertOK(c *check.C) { 75 // add store key 76 s.addAsserts() 77 78 st := s.d.Overlord().State() 79 80 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 81 buf := bytes.NewBuffer(asserts.Encode(acct)) 82 // Execute 83 req, err := http.NewRequest("POST", "/v2/assertions", buf) 84 c.Assert(err, check.IsNil) 85 rsp := s.syncReq(c, req, nil) 86 // Verify (external) 87 c.Check(rsp.Status, check.Equals, 200) 88 // Verify (internal) 89 st.Lock() 90 defer st.Unlock() 91 _, err = assertstate.DB(st).Find(asserts.AccountType, map[string]string{ 92 "account-id": acct.AccountID(), 93 }) 94 c.Check(err, check.IsNil) 95 } 96 97 func (s *assertsSuite) TestAssertStreamOK(c *check.C) { 98 st := s.d.Overlord().State() 99 100 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 101 buf := &bytes.Buffer{} 102 enc := asserts.NewEncoder(buf) 103 err := enc.Encode(acct) 104 c.Assert(err, check.IsNil) 105 err = enc.Encode(s.StoreSigning.StoreAccountKey("")) 106 c.Assert(err, check.IsNil) 107 108 // Execute 109 req, err := http.NewRequest("POST", "/v2/assertions", buf) 110 c.Assert(err, check.IsNil) 111 rsp := s.syncReq(c, req, nil) 112 // Verify (external) 113 c.Check(rsp.Status, check.Equals, 200) 114 // Verify (internal) 115 st.Lock() 116 defer st.Unlock() 117 _, err = assertstate.DB(st).Find(asserts.AccountType, map[string]string{ 118 "account-id": acct.AccountID(), 119 }) 120 c.Check(err, check.IsNil) 121 } 122 123 func (s *assertsSuite) TestAssertInvalid(c *check.C) { 124 // Setup 125 buf := bytes.NewBufferString("blargh") 126 req, err := http.NewRequest("POST", "/v2/assertions", buf) 127 c.Assert(err, check.IsNil) 128 s.asUserAuth(c, req) 129 130 rec := httptest.NewRecorder() 131 // Execute 132 s.serveHTTP(c, rec, req) 133 // Verify (external) 134 c.Check(rec.Code, check.Equals, 400) 135 c.Check(rec.Body.String(), testutil.Contains, 136 "cannot decode request body into assertions") 137 } 138 139 func (s *assertsSuite) TestAssertError(c *check.C) { 140 // Setup 141 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 142 buf := bytes.NewBuffer(asserts.Encode(acct)) 143 req, err := http.NewRequest("POST", "/v2/assertions", buf) 144 c.Assert(err, check.IsNil) 145 s.asUserAuth(c, req) 146 147 rec := httptest.NewRecorder() 148 // Execute 149 s.serveHTTP(c, rec, req) 150 // Verify (external) 151 c.Check(rec.Code, check.Equals, 400) 152 c.Check(rec.Body.String(), testutil.Contains, "assert failed") 153 } 154 155 func (s *assertsSuite) TestAssertsFindManyAll(c *check.C) { 156 acct := assertstest.NewAccount(s.StoreSigning, "developer1", map[string]interface{}{ 157 "account-id": "developer1-id", 158 }, "") 159 s.addAsserts(acct) 160 161 // Execute 162 req, err := http.NewRequest("GET", "/v2/assertions/account", nil) 163 c.Assert(err, check.IsNil) 164 s.asUserAuth(c, req) 165 166 rec := httptest.NewRecorder() 167 s.serveHTTP(c, rec, req) 168 // Verify 169 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 170 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/x.ubuntu.assertion; bundle=y") 171 c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "4") 172 dec := asserts.NewDecoder(rec.Body) 173 a1, err := dec.Decode() 174 c.Assert(err, check.IsNil) 175 c.Check(a1.Type(), check.Equals, asserts.AccountType) 176 177 a2, err := dec.Decode() 178 c.Assert(err, check.IsNil) 179 180 a3, err := dec.Decode() 181 c.Assert(err, check.IsNil) 182 183 a4, err := dec.Decode() 184 c.Assert(err, check.IsNil) 185 186 _, err = dec.Decode() 187 c.Assert(err, check.Equals, io.EOF) 188 189 ids := []string{a1.(*asserts.Account).AccountID(), a2.(*asserts.Account).AccountID(), a3.(*asserts.Account).AccountID(), a4.(*asserts.Account).AccountID()} 190 sort.Strings(ids) 191 c.Check(ids, check.DeepEquals, []string{"can0nical", "canonical", "developer1-id", "generic"}) 192 } 193 194 func (s *assertsSuite) TestAssertsFindManyFilter(c *check.C) { 195 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 196 s.addAsserts(acct) 197 198 // Execute 199 req, err := http.NewRequest("GET", "/v2/assertions/account?username=developer1", nil) 200 c.Assert(err, check.IsNil) 201 s.asUserAuth(c, req) 202 203 rec := httptest.NewRecorder() 204 s.serveHTTP(c, rec, req) 205 // Verify 206 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 207 c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "1") 208 dec := asserts.NewDecoder(rec.Body) 209 a1, err := dec.Decode() 210 c.Assert(err, check.IsNil) 211 c.Check(a1.Type(), check.Equals, asserts.AccountType) 212 c.Check(a1.(*asserts.Account).Username(), check.Equals, "developer1") 213 c.Check(a1.(*asserts.Account).AccountID(), check.Equals, acct.AccountID()) 214 _, err = dec.Decode() 215 c.Check(err, check.Equals, io.EOF) 216 } 217 218 func (s *assertsSuite) TestAssertsFindManyNoResults(c *check.C) { 219 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 220 s.addAsserts(acct) 221 222 // Execute 223 req, err := http.NewRequest("GET", "/v2/assertions/account?username=xyzzyx", nil) 224 c.Assert(err, check.IsNil) 225 s.asUserAuth(c, req) 226 227 rec := httptest.NewRecorder() 228 s.serveHTTP(c, rec, req) 229 // Verify 230 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 231 c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "0") 232 dec := asserts.NewDecoder(rec.Body) 233 _, err = dec.Decode() 234 c.Check(err, check.Equals, io.EOF) 235 } 236 237 func (s *assertsSuite) TestAssertsInvalidType(c *check.C) { 238 // Execute 239 req, err := http.NewRequest("GET", "/v2/assertions/foo", nil) 240 c.Assert(err, check.IsNil) 241 s.asUserAuth(c, req) 242 243 rec := httptest.NewRecorder() 244 s.serveHTTP(c, rec, req) 245 // Verify 246 c.Check(rec.Code, check.Equals, 400) 247 c.Check(rec.Body.String(), testutil.Contains, "invalid assert type") 248 } 249 250 func (s *assertsSuite) TestAssertsFindManyJSONFilter(c *check.C) { 251 s.testAssertsFindManyJSONFilter(c, "/v2/assertions/account?json=true&username=developer1") 252 } 253 254 func (s *assertsSuite) TestAssertsFindManyJSONFilterRemoteIsFalse(c *check.C) { 255 // setting "remote=false" is the defalt and should not change anything 256 s.testAssertsFindManyJSONFilter(c, "/v2/assertions/account?json=true&username=developer1&remote=false") 257 } 258 259 func (s *assertsSuite) testAssertsFindManyJSONFilter(c *check.C, urlPath string) { 260 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 261 s.addAsserts(acct) 262 263 // Execute 264 req, err := http.NewRequest("GET", urlPath, nil) 265 c.Assert(err, check.IsNil) 266 s.asUserAuth(c, req) 267 268 rec := httptest.NewRecorder() 269 s.serveHTTP(c, rec, req) 270 // Verify 271 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 272 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json") 273 274 var body map[string]interface{} 275 err = json.Unmarshal(rec.Body.Bytes(), &body) 276 c.Assert(err, check.IsNil) 277 c.Check(body["result"], check.DeepEquals, []interface{}{ 278 map[string]interface{}{ 279 "headers": acct.Headers(), 280 }, 281 }) 282 } 283 284 func (s *assertsSuite) TestAssertsFindManyJSONNoResults(c *check.C) { 285 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 286 s.addAsserts(acct) 287 288 // Execute 289 req, err := http.NewRequest("GET", "/v2/assertions/account?json=true&username=xyz", nil) 290 c.Assert(err, check.IsNil) 291 s.asUserAuth(c, req) 292 293 rec := httptest.NewRecorder() 294 s.serveHTTP(c, rec, req) 295 // Verify 296 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 297 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json") 298 299 var body map[string]interface{} 300 err = json.Unmarshal(rec.Body.Bytes(), &body) 301 c.Assert(err, check.IsNil) 302 c.Check(body["result"], check.DeepEquals, []interface{}{}) 303 } 304 305 func (s *assertsSuite) TestAssertsFindManyJSONWithBody(c *check.C) { 306 // add store key 307 s.addAsserts() 308 309 // Execute 310 req, err := http.NewRequest("GET", "/v2/assertions/account-key?json=true", nil) 311 c.Assert(err, check.IsNil) 312 s.asUserAuth(c, req) 313 314 rec := httptest.NewRecorder() 315 s.serveHTTP(c, rec, req) 316 // Verify 317 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 318 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json") 319 320 var got []string 321 var body map[string]interface{} 322 err = json.Unmarshal(rec.Body.Bytes(), &body) 323 c.Assert(err, check.IsNil) 324 for _, a := range body["result"].([]interface{}) { 325 h := a.(map[string]interface{})["headers"].(map[string]interface{}) 326 got = append(got, h["account-id"].(string)+"/"+h["name"].(string)) 327 // check body 328 l, err := strconv.Atoi(h["body-length"].(string)) 329 c.Assert(err, check.IsNil) 330 c.Check(a.(map[string]interface{})["body"], check.HasLen, l) 331 } 332 sort.Strings(got) 333 c.Check(got, check.DeepEquals, []string{"can0nical/root", "can0nical/store", "canonical/root", "generic/models"}) 334 } 335 336 func (s *assertsSuite) TestAssertsFindManyJSONHeadersOnly(c *check.C) { 337 // add store key 338 s.addAsserts() 339 340 // Execute 341 req, err := http.NewRequest("GET", "/v2/assertions/account-key?json=headers&account-id=can0nical", nil) 342 c.Assert(err, check.IsNil) 343 s.asUserAuth(c, req) 344 345 rec := httptest.NewRecorder() 346 s.serveHTTP(c, rec, req) 347 // Verify 348 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 349 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json") 350 351 var got []string 352 var body map[string]interface{} 353 err = json.Unmarshal(rec.Body.Bytes(), &body) 354 c.Assert(err, check.IsNil) 355 for _, a := range body["result"].([]interface{}) { 356 h := a.(map[string]interface{})["headers"].(map[string]interface{}) 357 got = append(got, h["account-id"].(string)+"/"+h["name"].(string)) 358 // check body absent 359 _, ok := a.(map[string]interface{})["body"] 360 c.Assert(ok, check.Equals, false) 361 } 362 sort.Strings(got) 363 c.Check(got, check.DeepEquals, []string{"can0nical/root", "can0nical/store"}) 364 } 365 366 func (s *assertsSuite) TestAssertsFindManyJSONInvalidParam(c *check.C) { 367 // add store key 368 s.addAsserts() 369 370 // Execute 371 req, err := http.NewRequest("GET", "/v2/assertions/account-key?json=header&account-id=can0nical", nil) 372 c.Assert(err, check.IsNil) 373 s.asUserAuth(c, req) 374 375 rec := httptest.NewRecorder() 376 s.serveHTTP(c, rec, req) 377 // Verify 378 c.Check(rec.Code, check.Equals, 400, check.Commentf("body %q", rec.Body)) 379 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json") 380 381 var rsp daemon.RespJSON 382 c.Assert(json.Unmarshal(rec.Body.Bytes(), &rsp), check.IsNil) 383 c.Check(rsp.Status, check.Equals, 400) 384 c.Check(rsp.Type, check.Equals, daemon.ResponseTypeError) 385 c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{ 386 "message": `"json" query parameter when used must be set to "true" or "headers"`, 387 }) 388 } 389 390 func (s *assertsSuite) TestAssertsFindManyJSONNopFilter(c *check.C) { 391 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 392 s.addAsserts(acct) 393 394 // Execute 395 req, err := http.NewRequest("GET", "/v2/assertions/account?json=false&username=developer1", nil) 396 c.Assert(err, check.IsNil) 397 s.asUserAuth(c, req) 398 399 rec := httptest.NewRecorder() 400 s.serveHTTP(c, rec, req) 401 // Verify 402 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 403 c.Check(rec.Header().Get("X-Ubuntu-Assertions-Count"), check.Equals, "1") 404 dec := asserts.NewDecoder(rec.Body) 405 a1, err := dec.Decode() 406 c.Assert(err, check.IsNil) 407 c.Check(a1.Type(), check.Equals, asserts.AccountType) 408 c.Check(a1.(*asserts.Account).Username(), check.Equals, "developer1") 409 c.Check(a1.(*asserts.Account).AccountID(), check.Equals, acct.AccountID()) 410 _, err = dec.Decode() 411 c.Check(err, check.Equals, io.EOF) 412 } 413 414 func (s *assertsSuite) TestAssertsFindManyRemoteInvalidParam(c *check.C) { 415 // Execute 416 req, err := http.NewRequest("GET", "/v2/assertions/account-key?remote=invalid&account-id=can0nical", nil) 417 c.Assert(err, check.IsNil) 418 s.asUserAuth(c, req) 419 420 rec := httptest.NewRecorder() 421 s.serveHTTP(c, rec, req) 422 // Verify 423 c.Check(rec.Code, check.Equals, 400, check.Commentf("body %q", rec.Body)) 424 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/json") 425 var rsp daemon.RespJSON 426 c.Assert(json.Unmarshal(rec.Body.Bytes(), &rsp), check.IsNil) 427 c.Check(rsp.Status, check.Equals, 400) 428 c.Check(rsp.Type, check.Equals, daemon.ResponseTypeError) 429 c.Check(rsp.Result, check.DeepEquals, map[string]interface{}{ 430 "message": `"remote" query parameter when used must be set to "true" or "false" or left unset`, 431 }) 432 } 433 434 func (s *assertsSuite) Assertion(at *asserts.AssertionType, headers []string, user *auth.UserState) (asserts.Assertion, error) { 435 return s.mockAssertionFn(at, headers, user) 436 } 437 438 func (s *assertsSuite) TestAssertsFindManyRemote(c *check.C) { 439 var assertFnCalled int 440 s.mockAssertionFn = func(at *asserts.AssertionType, headers []string, user *auth.UserState) (asserts.Assertion, error) { 441 assertFnCalled++ 442 c.Assert(at.Name, check.Equals, "account") 443 c.Assert(headers, check.DeepEquals, []string{"can0nical"}) 444 return assertstest.NewAccount(s.StoreSigning, "some-developer", nil, ""), nil 445 } 446 447 acct := assertstest.NewAccount(s.StoreSigning, "developer1", nil, "") 448 s.addAsserts(acct) 449 450 // Execute 451 req, err := http.NewRequest("GET", "/v2/assertions/account?remote=true&account-id=can0nical", nil) 452 c.Assert(err, check.IsNil) 453 s.asUserAuth(c, req) 454 455 rec := httptest.NewRecorder() 456 s.serveHTTP(c, rec, req) 457 // Verify 458 c.Check(assertFnCalled, check.Equals, 1) 459 c.Check(rec.Code, check.Equals, 200, check.Commentf("body %q", rec.Body)) 460 c.Check(rec.Header().Get("Content-Type"), check.Equals, "application/x.ubuntu.assertion; bundle=y") 461 462 data := rec.Body.Bytes() 463 c.Check(string(data), check.Matches, `(?ms)type: account 464 authority-id: can0nical 465 account-id: [a-zA-Z0-9]+ 466 display-name: Some-developer 467 timestamp: .* 468 username: some-developer 469 validation: unproven 470 .* 471 `) 472 473 }