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