github.com/SophiaGitHub/hello@v1.7.1-rc3/trust/trusts.go (about)

     1  package trust
     2  
     3  import (
     4  	"crypto/x509"
     5  	"errors"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/Sirupsen/logrus"
    16  	"github.com/docker/libtrust/trustgraph"
    17  )
    18  
    19  type TrustStore struct {
    20  	path          string
    21  	caPool        *x509.CertPool
    22  	graph         trustgraph.TrustGraph
    23  	expiration    time.Time
    24  	fetcher       *time.Timer
    25  	fetchTime     time.Duration
    26  	autofetch     bool
    27  	httpClient    *http.Client
    28  	baseEndpoints map[string]*url.URL
    29  
    30  	sync.RWMutex
    31  }
    32  
    33  // defaultFetchtime represents the starting duration to wait between
    34  // fetching sections of the graph.  Unsuccessful fetches should
    35  // increase time between fetching.
    36  const defaultFetchtime = 45 * time.Second
    37  
    38  var baseEndpoints = map[string]string{"official": "https://dvjy3tqbc323p.cloudfront.net/trust/official.json"}
    39  
    40  func NewTrustStore(path string) (*TrustStore, error) {
    41  	abspath, err := filepath.Abs(path)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	// Create base graph url map
    47  	endpoints := map[string]*url.URL{}
    48  	for name, endpoint := range baseEndpoints {
    49  		u, err := url.Parse(endpoint)
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  		endpoints[name] = u
    54  	}
    55  
    56  	// Load grant files
    57  	t := &TrustStore{
    58  		path:          abspath,
    59  		caPool:        nil,
    60  		httpClient:    &http.Client{},
    61  		fetchTime:     time.Millisecond,
    62  		baseEndpoints: endpoints,
    63  	}
    64  
    65  	if err := t.reload(); err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	return t, nil
    70  }
    71  
    72  func (t *TrustStore) reload() error {
    73  	t.Lock()
    74  	defer t.Unlock()
    75  
    76  	matches, err := filepath.Glob(filepath.Join(t.path, "*.json"))
    77  	if err != nil {
    78  		return err
    79  	}
    80  	statements := make([]*trustgraph.Statement, len(matches))
    81  	for i, match := range matches {
    82  		f, err := os.Open(match)
    83  		if err != nil {
    84  			return err
    85  		}
    86  		statements[i], err = trustgraph.LoadStatement(f, nil)
    87  		if err != nil {
    88  			f.Close()
    89  			return err
    90  		}
    91  		f.Close()
    92  	}
    93  	if len(statements) == 0 {
    94  		if t.autofetch {
    95  			logrus.Debugf("No grants, fetching")
    96  			t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
    97  		}
    98  		return nil
    99  	}
   100  
   101  	grants, expiration, err := trustgraph.CollapseStatements(statements, true)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	t.expiration = expiration
   107  	t.graph = trustgraph.NewMemoryGraph(grants)
   108  	logrus.Debugf("Reloaded graph with %d grants expiring at %s", len(grants), expiration)
   109  
   110  	if t.autofetch {
   111  		nextFetch := expiration.Sub(time.Now())
   112  		if nextFetch < 0 {
   113  			nextFetch = defaultFetchtime
   114  		} else {
   115  			nextFetch = time.Duration(0.8 * (float64)(nextFetch))
   116  		}
   117  		t.fetcher = time.AfterFunc(nextFetch, t.fetch)
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  func (t *TrustStore) fetchBaseGraph(u *url.URL) (*trustgraph.Statement, error) {
   124  	req := &http.Request{
   125  		Method:     "GET",
   126  		URL:        u,
   127  		Proto:      "HTTP/1.1",
   128  		ProtoMajor: 1,
   129  		ProtoMinor: 1,
   130  		Header:     make(http.Header),
   131  		Body:       nil,
   132  		Host:       u.Host,
   133  	}
   134  
   135  	resp, err := t.httpClient.Do(req)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	if resp.StatusCode == 404 {
   140  		return nil, errors.New("base graph does not exist")
   141  	}
   142  
   143  	defer resp.Body.Close()
   144  
   145  	return trustgraph.LoadStatement(resp.Body, t.caPool)
   146  }
   147  
   148  // fetch retrieves updated base graphs.  This function cannot error, it
   149  // should only log errors
   150  func (t *TrustStore) fetch() {
   151  	t.Lock()
   152  	defer t.Unlock()
   153  
   154  	if t.autofetch && t.fetcher == nil {
   155  		// Do nothing ??
   156  		return
   157  	}
   158  
   159  	fetchCount := 0
   160  	for bg, ep := range t.baseEndpoints {
   161  		statement, err := t.fetchBaseGraph(ep)
   162  		if err != nil {
   163  			logrus.Infof("Trust graph fetch failed: %s", err)
   164  			continue
   165  		}
   166  		b, err := statement.Bytes()
   167  		if err != nil {
   168  			logrus.Infof("Bad trust graph statement: %s", err)
   169  			continue
   170  		}
   171  		// TODO check if value differs
   172  		if err := ioutil.WriteFile(path.Join(t.path, bg+".json"), b, 0600); err != nil {
   173  			logrus.Infof("Error writing trust graph statement: %s", err)
   174  		}
   175  		fetchCount++
   176  	}
   177  	logrus.Debugf("Fetched %d base graphs at %s", fetchCount, time.Now())
   178  
   179  	if fetchCount > 0 {
   180  		go func() {
   181  			if err := t.reload(); err != nil {
   182  				logrus.Infof("Reload of trust graph failed: %s", err)
   183  			}
   184  		}()
   185  		t.fetchTime = defaultFetchtime
   186  		t.fetcher = nil
   187  	} else if t.autofetch {
   188  		maxTime := 10 * defaultFetchtime
   189  		t.fetchTime = time.Duration(1.5 * (float64)(t.fetchTime+time.Second))
   190  		if t.fetchTime > maxTime {
   191  			t.fetchTime = maxTime
   192  		}
   193  		t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
   194  	}
   195  }