github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/asserts.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 client 21 22 import ( 23 "bytes" 24 "context" 25 "fmt" 26 "io" 27 "net/url" 28 "strconv" 29 30 "golang.org/x/xerrors" 31 32 "github.com/snapcore/snapd/asserts" // for parsing 33 "github.com/snapcore/snapd/snap" 34 ) 35 36 // Ack tries to add an assertion to the system assertion 37 // database. To succeed the assertion must be valid, its signature 38 // verified with a known public key and the assertion consistent with 39 // and its prerequisite in the database. 40 func (client *Client) Ack(b []byte) error { 41 var rsp interface{} 42 if _, err := client.doSync("POST", "/v2/assertions", nil, nil, bytes.NewReader(b), &rsp); err != nil { 43 return err 44 } 45 46 return nil 47 } 48 49 // AssertionTypes returns a list of assertion type names. 50 func (client *Client) AssertionTypes() ([]string, error) { 51 var types struct { 52 Types []string `json:"types"` 53 } 54 _, err := client.doSync("GET", "/v2/assertions", nil, nil, nil, &types) 55 if err != nil { 56 fmt := "cannot get assertion type names: %w" 57 return nil, xerrors.Errorf(fmt, err) 58 } 59 60 return types.Types, nil 61 } 62 63 // KnownOptions represent the options of the Known call. 64 type KnownOptions struct { 65 // If Remote is true, the store is queried to find the assertion 66 Remote bool 67 } 68 69 // Known queries assertions with type assertTypeName and matching assertion headers. 70 func (client *Client) Known(assertTypeName string, headers map[string]string, opts *KnownOptions) ([]asserts.Assertion, error) { 71 if opts == nil { 72 opts = &KnownOptions{} 73 } 74 75 path := fmt.Sprintf("/v2/assertions/%s", assertTypeName) 76 q := url.Values{} 77 78 if len(headers) > 0 { 79 for k, v := range headers { 80 q.Set(k, v) 81 } 82 } 83 if opts.Remote { 84 q.Set("remote", "true") 85 } 86 87 response, cancel, err := client.rawWithTimeout(context.Background(), "GET", path, q, nil, nil, nil) 88 if err != nil { 89 fmt := "failed to query assertions: %w" 90 return nil, xerrors.Errorf(fmt, err) 91 } 92 defer cancel() 93 defer response.Body.Close() 94 if response.StatusCode != 200 { 95 return nil, parseError(response) 96 } 97 98 sanityCount, err := strconv.Atoi(response.Header.Get("X-Ubuntu-Assertions-Count")) 99 if err != nil { 100 return nil, fmt.Errorf("invalid assertions count") 101 } 102 103 dec := asserts.NewDecoder(response.Body) 104 105 asserts := []asserts.Assertion{} 106 107 // TODO: make sure asserts can decode and deal with unknown types 108 for { 109 a, err := dec.Decode() 110 if err == io.EOF { 111 break 112 } 113 if err != nil { 114 return nil, fmt.Errorf("failed to decode assertions: %v", err) 115 } 116 asserts = append(asserts, a) 117 } 118 119 if len(asserts) != sanityCount { 120 return nil, fmt.Errorf("response did not have the expected number of assertions") 121 } 122 123 return asserts, nil 124 } 125 126 // StoreAccount returns the full store account info for the specified accountID 127 func (client *Client) StoreAccount(accountID string) (*snap.StoreAccount, error) { 128 assertions, err := client.Known("account", map[string]string{"account-id": accountID}, nil) 129 if err != nil { 130 return nil, err 131 } 132 switch len(assertions) { 133 case 1: 134 // happy case, break out of the switch 135 case 0: 136 return nil, fmt.Errorf("no assertion found for account-id %s", accountID) 137 default: 138 // unknown how this could happen... 139 return nil, fmt.Errorf("multiple assertions for account-id %s", accountID) 140 } 141 142 acct, ok := assertions[0].(*asserts.Account) 143 if !ok { 144 return nil, fmt.Errorf("incorrect type of account assertion returned") 145 } 146 return &snap.StoreAccount{ 147 ID: acct.AccountID(), 148 Username: acct.Username(), 149 DisplayName: acct.DisplayName(), 150 Validation: acct.Validation(), 151 }, nil 152 }