1 package middleware
2
3 import (
4 "context"
5 "errors"
6 "net/http"
7 "testing"
8
9 "github.com/stretchr/testify/require"
10
11 "github.com/go-openapi/runtime"
12 )
13
14 type countAuthenticator struct {
15 count int
16 applies bool
17 principal interface{}
18 err error
19 }
20
21 func (c *countAuthenticator) Authenticate(_ interface{}) (bool, interface{}, error) {
22 c.count++
23 return c.applies, c.principal, c.err
24 }
25
26 func newCountAuthenticator(applies bool, principal interface{}, err error) *countAuthenticator {
27 return &countAuthenticator{applies: applies, principal: principal, err: err}
28 }
29
30 var (
31 successAuth = runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) {
32 return true, "the user", nil
33 })
34 failAuth = runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) {
35 return true, nil, errors.New("unauthenticated")
36 })
37 noApplyAuth = runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) {
38 return false, nil, nil
39 })
40 )
41
42 func TestAuthenticateSingle(t *testing.T) {
43 ra := RouteAuthenticator{
44 Authenticator: map[string]runtime.Authenticator{
45 "auth1": successAuth,
46 },
47 Schemes: []string{"auth1"},
48 Scopes: map[string][]string{"auth1": nil},
49 }
50 ras := RouteAuthenticators([]RouteAuthenticator{ra})
51
52 require.False(t, ras.AllowsAnonymous())
53
54 req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
55 route := &MatchedRoute{}
56 ok, prin, err := ras.Authenticate(req, route)
57 require.NoError(t, err)
58 require.True(t, ok)
59 require.Equal(t, "the user", prin)
60
61 require.Equal(t, ra, *route.Authenticator)
62 }
63
64 func TestAuthenticateLogicalOr(t *testing.T) {
65 ra1 := RouteAuthenticator{
66 Authenticator: map[string]runtime.Authenticator{
67 "auth1": noApplyAuth,
68 },
69 Schemes: []string{"auth1"},
70 Scopes: map[string][]string{"auth1": nil},
71 }
72 ra2 := RouteAuthenticator{
73 Authenticator: map[string]runtime.Authenticator{
74 "auth2": successAuth,
75 },
76 Schemes: []string{"auth2"},
77 Scopes: map[string][]string{"auth2": nil},
78 }
79
80 ras := RouteAuthenticators([]RouteAuthenticator{ra1, ra2})
81 require.False(t, ras.AllowsAnonymous())
82
83 req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
84 route := &MatchedRoute{}
85 ok, prin, err := ras.Authenticate(req, route)
86 require.NoError(t, err)
87 require.True(t, ok)
88 require.Equal(t, "the user", prin)
89
90 require.Equal(t, ra2, *route.Authenticator)
91
92
93 ras = RouteAuthenticators([]RouteAuthenticator{ra2, ra1})
94 require.False(t, ras.AllowsAnonymous())
95
96 req, _ = http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
97 route = &MatchedRoute{}
98 ok, prin, err = ras.Authenticate(req, route)
99 require.NoError(t, err)
100
101 require.True(t, ok)
102 require.Equal(t, "the user", prin)
103 require.Equal(t, ra2, *route.Authenticator)
104 }
105
106 func TestAuthenticateLogicalAnd(t *testing.T) {
107 ra1 := RouteAuthenticator{
108 Authenticator: map[string]runtime.Authenticator{
109 "auth1": noApplyAuth,
110 },
111 Schemes: []string{"auth1"},
112 Scopes: map[string][]string{"auth1": nil},
113 }
114 authorizer := newCountAuthenticator(true, "the user", nil)
115 ra2 := RouteAuthenticator{
116 Authenticator: map[string]runtime.Authenticator{
117 "auth2": authorizer,
118 "auth3": authorizer,
119 },
120 Schemes: []string{"auth2", "auth3"},
121 Scopes: map[string][]string{"auth2": nil},
122 }
123 ras := RouteAuthenticators([]RouteAuthenticator{ra1, ra2})
124 require.False(t, ras.AllowsAnonymous())
125
126 req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
127 require.NoError(t, err)
128 route := &MatchedRoute{}
129 ok, prin, err := ras.Authenticate(req, route)
130 require.NoError(t, err)
131 require.True(t, ok)
132 require.Equal(t, "the user", prin)
133
134 require.Equal(t, ra2, *route.Authenticator)
135 require.Equal(t, 2, authorizer.count)
136
137 var count int
138 successA := runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) {
139 count++
140 return true, "the user", nil
141 })
142 failA := runtime.AuthenticatorFunc(func(_ interface{}) (bool, interface{}, error) {
143 count++
144 return true, nil, errors.New("unauthenticated")
145 })
146
147 ra3 := RouteAuthenticator{
148 Authenticator: map[string]runtime.Authenticator{
149 "auth2": successA,
150 "auth3": failA,
151 "auth4": successA,
152 },
153 Schemes: []string{"auth2", "auth3", "auth4"},
154 Scopes: map[string][]string{"auth2": nil},
155 }
156 ras = RouteAuthenticators([]RouteAuthenticator{ra1, ra3})
157
158 require.False(t, ras.AllowsAnonymous())
159
160 req, _ = http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
161 route = &MatchedRoute{}
162 ok, prin, err = ras.Authenticate(req, route)
163 require.Error(t, err)
164 require.True(t, ok)
165 require.Nil(t, prin)
166
167 require.Equal(t, ra3, *route.Authenticator)
168 require.Equal(t, 2, count)
169
170 ra4 := RouteAuthenticator{
171 Authenticator: map[string]runtime.Authenticator{
172 "auth2": successA,
173 "auth3": successA,
174 "auth4": failA,
175 },
176 Schemes: []string{"auth2", "auth3", "auth4"},
177 Scopes: map[string][]string{"auth2": nil},
178 }
179 ras = RouteAuthenticators([]RouteAuthenticator{ra1, ra4})
180
181 require.False(t, ras.AllowsAnonymous())
182
183 req, _ = http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
184 route = &MatchedRoute{}
185 ok, prin, err = ras.Authenticate(req, route)
186 require.Error(t, err)
187 require.True(t, ok)
188 require.Nil(t, prin)
189
190 require.Equal(t, ra4, *route.Authenticator)
191 require.Equal(t, 5, count)
192 }
193
194 func TestAuthenticateOptional(t *testing.T) {
195 ra1 := RouteAuthenticator{
196 Authenticator: map[string]runtime.Authenticator{
197 "auth1": noApplyAuth,
198 },
199 Schemes: []string{"auth1"},
200 Scopes: map[string][]string{"auth1": nil},
201 }
202 ra2 := RouteAuthenticator{
203 allowAnonymous: true,
204 Schemes: []string{""},
205 Scopes: map[string][]string{"": {}},
206 }
207
208 ra3 := RouteAuthenticator{
209 Authenticator: map[string]runtime.Authenticator{
210 "auth2": noApplyAuth,
211 },
212 Schemes: []string{"auth2"},
213 Scopes: map[string][]string{"auth2": nil},
214 }
215
216 ras := RouteAuthenticators([]RouteAuthenticator{ra1, ra2, ra3})
217 require.True(t, ras.AllowsAnonymous())
218
219 req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
220 route := &MatchedRoute{}
221 ok, prin, err := ras.Authenticate(req, route)
222 require.NoError(t, err)
223 require.True(t, ok)
224 require.Nil(t, prin)
225
226 require.Equal(t, ra2, *route.Authenticator)
227
228 ra4 := RouteAuthenticator{
229 Authenticator: map[string]runtime.Authenticator{
230 "auth1": noApplyAuth,
231 },
232 Schemes: []string{"auth1"},
233 Scopes: map[string][]string{"auth1": nil},
234 }
235 ra5 := RouteAuthenticator{
236 allowAnonymous: true,
237 }
238
239 ra6 := RouteAuthenticator{
240 Authenticator: map[string]runtime.Authenticator{
241 "auth2": failAuth,
242 },
243 Schemes: []string{"auth2"},
244 Scopes: map[string][]string{"auth2": nil},
245 }
246
247 ras = RouteAuthenticators([]RouteAuthenticator{ra4, ra5, ra6})
248 require.True(t, ras.AllowsAnonymous())
249
250 req, _ = http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
251 route = &MatchedRoute{}
252 ok, prin, err = ras.Authenticate(req, route)
253 require.Error(t, err)
254 require.True(t, ok)
255 require.Nil(t, prin)
256
257 require.Equal(t, ra6, *route.Authenticator)
258 }
259
View as plain text