-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathcredentials.go
140 lines (119 loc) · 3.39 KB
/
credentials.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright 2022-2024 Sauce Labs Inc., all rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
package forwarder
import (
"errors"
"fmt"
"net"
"net/url"
"github.com/saucelabs/forwarder/log"
)
type CredentialsMatcher struct {
hostport map[string]*url.Userinfo
host map[string]*url.Userinfo
port map[string]*url.Userinfo
global *url.Userinfo
log log.Logger
}
func NewCredentialsMatcher(credentials []*HostPortUser, log log.Logger) (*CredentialsMatcher, error) {
if len(credentials) == 0 {
return nil, nil //nolint:nilnil // nil is a valid value
}
m := &CredentialsMatcher{
hostport: make(map[string]*url.Userinfo),
host: make(map[string]*url.Userinfo),
port: make(map[string]*url.Userinfo),
log: log,
}
for i, hpu := range credentials {
withRowInfo := func(err error) error {
return fmt.Errorf("%w at pos %d", err, i) //nolint:scopelint // false positive
}
if err := hpu.Validate(); err != nil {
return nil, withRowInfo(err)
}
switch {
case hpu.Host == "*" && hpu.Port == "0":
if m.global != nil {
return nil, withRowInfo(errors.New("duplicate global input"))
}
m.global = hpu.Userinfo
case hpu.Host == "*":
if _, ok := m.port[hpu.Port]; ok {
return nil, withRowInfo(fmt.Errorf("duplicate wildcard host with port %s credentis", hpu.Port))
}
m.port[hpu.Port] = hpu.Userinfo
case hpu.Port == "0":
if _, ok := m.host[hpu.Host]; ok {
return nil, withRowInfo(fmt.Errorf("duplicate wildcard port with host %s credentis", hpu.Host))
}
m.host[hpu.Host] = hpu.Userinfo
default:
hostport := net.JoinHostPort(hpu.Host, hpu.Port)
if _, ok := m.hostport[hostport]; ok {
return nil, errors.New("duplicate input")
}
m.hostport[hostport] = hpu.Userinfo
}
}
return m, nil
}
// MatchURL adds standard http and https ports if they are missing in URL and calls Match function.
func (m *CredentialsMatcher) MatchURL(u *url.URL) *url.Userinfo {
if m == nil || u == nil {
return nil
}
const (
httpPort = 80
httpsPort = 443
)
hostport := u.Host
if u.Port() == "" {
switch u.Scheme {
case "http":
hostport = fmt.Sprintf("%s:%d", u.Host, httpPort)
case "https":
hostport = fmt.Sprintf("%s:%d", u.Host, httpsPort)
default:
m.log.Errorf("cannot to determine port for %s", u.Redacted())
return nil
}
}
return m.Match(hostport)
}
// Match `hostport` to one of the configured input.
// Priority is exact Match, then host, then port, then global wildcard.
func (m *CredentialsMatcher) Match(hostport string) *url.Userinfo {
if m == nil {
return nil
}
if u, ok := m.hostport[hostport]; ok {
m.log.Debugf(hostport)
return u
}
host, port, err := net.SplitHostPort(hostport)
if err != nil {
m.log.Infof("invalid hostport %s", hostport)
return nil
}
// Host wildcard - check the port only.
if u, ok := m.port[port]; ok {
m.log.Debugf("host=* port=%s", port)
return u
}
// Port wildcard - check the host only.
if u, ok := m.host[host]; ok {
m.log.Debugf("host=%s port=*", host)
return u
}
// Log whether the global wildcard is set.
// This is a very esoteric use case. It's only added to support a legacy implementation.
if m.global != nil {
m.log.Debugf("global wildcard")
return m.global
}
return nil
}