1// Copyright (C) 2019 Storj Labs, Inc.
2// See LICENSE for copying information.
3
4package trust
5
6import (
7	"context"
8	"regexp"
9
10	"github.com/zeebo/errs"
11)
12
13// Entry represents a trust entry.
14type Entry struct {
15	// SatelliteURL is the URL of the satellite
16	SatelliteURL SatelliteURL
17
18	// Authoritative indicates whether this entry came from an authoritative
19	// source. This impacts how URLS are aggregated.
20	Authoritative bool `json:"authoritative"`
21}
22
23// Source is a trust source for trusted Satellites.
24type Source interface {
25	// String is the string representation of the source. It is used as a key
26	// into the cache.
27	String() string
28
29	// Static returns true if the source is static. Static sources are not cached.
30	Static() bool
31
32	// FetchEntries returns the list of trust entries from the source.
33	FetchEntries(context.Context) ([]Entry, error)
34}
35
36// NewSource takes a configuration string returns a Source for that string.
37func NewSource(config string) (Source, error) {
38	schema, ok := isReserved(config)
39	if ok {
40		switch schema {
41		case "http", "https":
42			return NewHTTPSource(config)
43		case "storj":
44			return NewStaticURLSource(config)
45		default:
46			return nil, errs.New("unsupported schema %q", schema)
47		}
48	}
49
50	if isProbablySatelliteURL(config) {
51		return NewStaticURLSource(config)
52	}
53
54	return NewFileSource(config), nil
55}
56
57var reReserved = regexp.MustCompile(`^([a-zA-Z]{2,})://`)
58
59// isReserved returns the true if the string is within the reserved namespace
60// for trust sources, i.e. things that look like a URI scheme. Single letter
61// schemes are not in the reserved namespace since those collide with paths
62// starting with Windows drive letters.
63func isReserved(s string) (schema string, ok bool) {
64	m := reReserved.FindStringSubmatch(s)
65	if m == nil {
66		return "", false
67	}
68	return m[1], true
69}
70
71// reProbablySatelliteURL matches config strings that are (intended, but
72// possibly misconfigured) satellite URLs, like the following:
73//
74//  - @
75//  - id@
76//  - host:9999
77//  - id@host:9999
78var reProbablySatelliteURL = regexp.MustCompile(`@|(^[^/\\]{2,}:\d+$)`)
79
80func isProbablySatelliteURL(s string) bool {
81	// Painful esoteric paths to consider if you want to change the regex. None
82	// of the paths below should be parsed as satellite URLs, which the
83	// exception of the last one, which would fail since it does not contain an
84	// ID portion but would fail with good diagnostics.
85	// 1. http://basic:auth@example.com
86	// 2. c:/windows
87	// 3. c:\\windows
88	// 3. \\?\c:\\windows
89	// 4. probably other nightmarish windows paths
90	// 5. /posix/paths:are/terrible
91	// 6. posix.paths.are.really.terrible:7777
92	return reProbablySatelliteURL.MatchString(s)
93}
94